privacycash 1.0.21 → 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/getUtxosSPL.d.ts +3 -2
- package/dist/getUtxosSPL.js +37 -12
- package/dist/index.d.ts +35 -1
- package/dist/index.js +68 -5
- package/dist/utils/constants.d.ts +12 -0
- package/dist/utils/constants.js +34 -0
- 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/getUtxosSPL.ts +44 -15
- package/src/index.ts +83 -5
- package/src/utils/constants.ts +45 -1
- package/src/withdrawSPL.ts +46 -26
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
|
+
console.log(' ####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[] = [];
|
|
@@ -150,17 +163,20 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
|
|
|
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
165
|
// reorgnize
|
|
153
|
-
|
|
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
|
|
154
172
|
}
|
|
155
173
|
|
|
156
|
-
async function fetchUserUtxos({
|
|
157
|
-
publicKey: PublicKey,
|
|
158
|
-
connection: Connection,
|
|
174
|
+
async function fetchUserUtxos({ url, storage, encryptionService, publicKey_ata, tokenName }: {
|
|
159
175
|
url: string,
|
|
160
176
|
encryptionService: EncryptionService,
|
|
161
177
|
storage: Storage,
|
|
162
178
|
publicKey_ata: PublicKey
|
|
163
|
-
|
|
179
|
+
tokenName: string
|
|
164
180
|
}): Promise<{
|
|
165
181
|
encryptedOutputs: string[],
|
|
166
182
|
utxos: Utxo[],
|
|
@@ -212,7 +228,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
|
|
|
212
228
|
|
|
213
229
|
|
|
214
230
|
let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
|
|
215
|
-
let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm)
|
|
231
|
+
let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
|
|
216
232
|
decryptionTaskFinished += encryptedOutputs.length
|
|
217
233
|
logger.debug('batchReslen', batchRes.length)
|
|
218
234
|
for (let i = 0; i < batchRes.length; i++) {
|
|
@@ -230,7 +246,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
|
|
|
230
246
|
if (decryptionTaskFinished % 100 == 0) {
|
|
231
247
|
logger.info(`(decrypting cached utxo: ${decryptionTaskFinished + 1}/${decryptionTaskTotal}...)`)
|
|
232
248
|
}
|
|
233
|
-
let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm)
|
|
249
|
+
let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
|
|
234
250
|
decryptionTaskFinished += cachedEncryptedOutputs.length
|
|
235
251
|
logger.debug('cachedbatchReslen', batchRes.length, ' source', cachedEncryptedOutputs.length)
|
|
236
252
|
for (let i = 0; i < batchRes.length; i++) {
|
|
@@ -346,11 +362,23 @@ async function areUtxosSpent(
|
|
|
346
362
|
// Calculate total balance
|
|
347
363
|
export function getBalanceFromUtxosSPL(utxos: Utxo[]): {
|
|
348
364
|
base_units: number
|
|
365
|
+
amount: number
|
|
349
366
|
/** @deprecated use base_units instead */
|
|
350
367
|
lamports: number
|
|
351
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
|
+
}
|
|
352
376
|
const totalBalance = utxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
|
|
353
|
-
return {
|
|
377
|
+
return {
|
|
378
|
+
base_units: totalBalance.toNumber(),
|
|
379
|
+
lamports: totalBalance.toNumber(),
|
|
380
|
+
amount: totalBalance.toNumber() / token.units_per_token
|
|
381
|
+
}
|
|
354
382
|
}
|
|
355
383
|
|
|
356
384
|
// Decrypt single output to Utxo
|
|
@@ -454,6 +482,7 @@ async function decrypt_outputs(
|
|
|
454
482
|
encryptionService: EncryptionService,
|
|
455
483
|
utxoKeypair: UtxoKeypair,
|
|
456
484
|
lightWasm: any,
|
|
485
|
+
tokenName: string
|
|
457
486
|
): Promise<DecryptRes[]> {
|
|
458
487
|
let results: DecryptRes[] = [];
|
|
459
488
|
|
|
@@ -485,7 +514,7 @@ async function decrypt_outputs(
|
|
|
485
514
|
let url = RELAYER_API_URL + `/utxos/indices`
|
|
486
515
|
let res = await fetch(url, {
|
|
487
516
|
method: 'POST', headers: { "Content-Type": "application/json" },
|
|
488
|
-
body: JSON.stringify({ encrypted_outputs, token:
|
|
517
|
+
body: JSON.stringify({ encrypted_outputs, token: tokenName })
|
|
489
518
|
})
|
|
490
519
|
let j = await res.json()
|
|
491
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(
|
package/src/utils/constants.ts
CHANGED
|
@@ -25,4 +25,48 @@ export const SIGN_MESSAGE = `Privacy Money account sign in`
|
|
|
25
25
|
export const LSK_FETCH_OFFSET = 'fetch_offset'
|
|
26
26
|
export const LSK_ENCRYPTED_OUTPUTS = 'encrypted_outputs'
|
|
27
27
|
|
|
28
|
-
export const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
|
|
28
|
+
export const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
|
|
29
|
+
|
|
30
|
+
const tokenList = ['sol', 'usdc', 'usdt', 'zec', 'ore'] as const;
|
|
31
|
+
export type TokenList = typeof tokenList[number];
|
|
32
|
+
const splList = ['usdc', 'usdt', 'ore'] as const;
|
|
33
|
+
export type SplList = typeof splList[number];
|
|
34
|
+
export type Token = {
|
|
35
|
+
name: 'sol' | 'usdc' | 'usdt' | 'zec' | 'ore'
|
|
36
|
+
prefix: string
|
|
37
|
+
units_per_token: number
|
|
38
|
+
pubkey: PublicKey
|
|
39
|
+
}
|
|
40
|
+
export const tokens: Token[] = [
|
|
41
|
+
{
|
|
42
|
+
name: 'sol',
|
|
43
|
+
pubkey: new PublicKey('So11111111111111111111111111111111111111112'),
|
|
44
|
+
prefix: '',
|
|
45
|
+
units_per_token: 1e9
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'usdc',
|
|
49
|
+
pubkey: process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
|
50
|
+
prefix: 'usdc_',
|
|
51
|
+
units_per_token: 1e6
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'usdt',
|
|
55
|
+
pubkey: process.env.NEXT_PUBLIC_USDT_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDT_MINT) : new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'),
|
|
56
|
+
prefix: 'usdt_',
|
|
57
|
+
units_per_token: 1e6
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'zec',
|
|
61
|
+
pubkey: process.env.NEXT_PUBLIC_ZEC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ZEC_MINT) : new PublicKey('A7bdiYdS5GjqGFtxf17ppRHtDKPkkRqbKtR27dxvQXaS'),
|
|
62
|
+
prefix: 'zec_',
|
|
63
|
+
units_per_token: 1e8
|
|
64
|
+
}
|
|
65
|
+
,
|
|
66
|
+
{
|
|
67
|
+
name: 'ore',
|
|
68
|
+
pubkey: process.env.NEXT_PUBLIC_ORE_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ORE_MINT) : new PublicKey('oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp'),
|
|
69
|
+
prefix: 'ore_',
|
|
70
|
+
units_per_token: 1e11
|
|
71
|
+
}
|
|
72
|
+
]
|
package/src/withdrawSPL.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as hasher from '@lightprotocol/hasher.rs';
|
|
|
6
6
|
import { Utxo } from './models/utxo.js';
|
|
7
7
|
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
8
8
|
|
|
9
|
-
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL, MERKLE_TREE_DEPTH, PROGRAM_ID } from './utils/constants.js';
|
|
9
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL, MERKLE_TREE_DEPTH, PROGRAM_ID, SplList, tokens } from './utils/constants.js';
|
|
10
10
|
import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
|
|
11
11
|
import { fetchMerkleProof, findNullifierPDAs, getProgramAccounts, queryRemoteTreeState, findCrossCheckNullifierPDAs, getMintAddressField, getExtDataHash } from './utils/utils.js';
|
|
12
12
|
|
|
@@ -20,7 +20,6 @@ import { getAssociatedTokenAddressSync, getMint } from '@solana/spl-token';
|
|
|
20
20
|
// Function to submit withdraw request to indexer backend
|
|
21
21
|
async function submitWithdrawToIndexer(params: any): Promise<string> {
|
|
22
22
|
try {
|
|
23
|
-
|
|
24
23
|
const response = await fetch(`${RELAYER_API_URL}/withdraw/spl`, {
|
|
25
24
|
method: 'POST',
|
|
26
25
|
headers: {
|
|
@@ -48,43 +47,64 @@ async function submitWithdrawToIndexer(params: any): Promise<string> {
|
|
|
48
47
|
type WithdrawParams = {
|
|
49
48
|
publicKey: PublicKey,
|
|
50
49
|
connection: Connection,
|
|
51
|
-
base_units
|
|
50
|
+
base_units?: number,
|
|
51
|
+
amount?: number,
|
|
52
52
|
keyBasePath: string,
|
|
53
53
|
encryptionService: EncryptionService,
|
|
54
54
|
lightWasm: hasher.LightWasm,
|
|
55
55
|
recipient: PublicKey,
|
|
56
|
-
mintAddress: PublicKey,
|
|
57
|
-
storage: Storage
|
|
56
|
+
mintAddress: PublicKey | string,
|
|
57
|
+
storage: Storage,
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, connection, base_units, encryptionService, keyBasePath, mintAddress }: WithdrawParams) {
|
|
61
|
-
|
|
60
|
+
export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, connection, base_units, amount, encryptionService, keyBasePath, mintAddress }: WithdrawParams) {
|
|
61
|
+
if (typeof mintAddress == 'string') {
|
|
62
|
+
mintAddress = new PublicKey(mintAddress)
|
|
63
|
+
}
|
|
64
|
+
let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString())
|
|
65
|
+
if (!token) {
|
|
66
|
+
throw new Error('token not found: ' + mintAddress.toString())
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (amount) {
|
|
70
|
+
base_units = amount * token.units_per_token
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!base_units) {
|
|
74
|
+
throw new Error('You must input at leaset one of "base_units" or "amount"')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
let mintInfo = await getMint(connection, token.pubkey)
|
|
62
79
|
let units_per_token = 10 ** mintInfo.decimals
|
|
63
80
|
|
|
64
81
|
let withdraw_fee_rate = await getConfig('withdraw_fee_rate')
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
let withdraw_rent_fees = await getConfig('rent_fees')
|
|
83
|
+
let token_rent_fee = withdraw_rent_fees[token.name]
|
|
84
|
+
if (!token_rent_fee) {
|
|
85
|
+
throw new Error('can not find token_rent_fee for ' + token.name)
|
|
86
|
+
}
|
|
87
|
+
let fee_base_units = Math.floor(base_units * withdraw_fee_rate + units_per_token * token_rent_fee)
|
|
68
88
|
base_units -= fee_base_units
|
|
69
89
|
|
|
70
90
|
if (base_units <= 0) {
|
|
71
|
-
throw new Error('withdraw amount too low')
|
|
91
|
+
throw new Error('withdraw amount too low, at least ' + fee_base_units / token_rent_fee)
|
|
72
92
|
}
|
|
73
93
|
let isPartial = false
|
|
74
94
|
|
|
75
95
|
let recipient_ata = getAssociatedTokenAddressSync(
|
|
76
|
-
|
|
96
|
+
token.pubkey,
|
|
77
97
|
recipient,
|
|
78
98
|
true
|
|
79
99
|
);
|
|
80
100
|
|
|
81
101
|
let feeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
82
|
-
|
|
102
|
+
token.pubkey,
|
|
83
103
|
FEE_RECIPIENT,
|
|
84
104
|
true
|
|
85
105
|
);
|
|
86
106
|
let signerTokenAccount = getAssociatedTokenAddressSync(
|
|
87
|
-
|
|
107
|
+
token.pubkey,
|
|
88
108
|
publicKey
|
|
89
109
|
);
|
|
90
110
|
|
|
@@ -93,14 +113,14 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
93
113
|
|
|
94
114
|
// Derive tree account PDA with mint address for SPL (different from SOL version)
|
|
95
115
|
const [treeAccount] = PublicKey.findProgramAddressSync(
|
|
96
|
-
[Buffer.from('merkle_tree'),
|
|
116
|
+
[Buffer.from('merkle_tree'), token.pubkey.toBuffer()],
|
|
97
117
|
PROGRAM_ID
|
|
98
118
|
);
|
|
99
119
|
|
|
100
120
|
const { globalConfigAccount, treeTokenAccount } = getProgramAccounts()
|
|
101
121
|
|
|
102
122
|
// Get current tree state
|
|
103
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(
|
|
123
|
+
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
|
|
104
124
|
logger.debug(`Using tree root: ${root}`);
|
|
105
125
|
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
106
126
|
|
|
@@ -119,7 +139,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
119
139
|
logger.debug('\nFetching existing UTXOs...');
|
|
120
140
|
const mintUtxos = await getUtxosSPL({ connection, publicKey, encryptionService, storage, mintAddress });
|
|
121
141
|
|
|
122
|
-
logger.debug(`Found ${mintUtxos.length} total UTXOs`);
|
|
142
|
+
logger.debug(`Found ${mintUtxos.length} total UTXOs for ${token.name}`);
|
|
123
143
|
|
|
124
144
|
// Calculate and log total unspent UTXO balance
|
|
125
145
|
const totalUnspentBalance = mintUtxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
|
|
@@ -138,7 +158,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
138
158
|
lightWasm,
|
|
139
159
|
keypair: utxoKeypair,
|
|
140
160
|
amount: '0',
|
|
141
|
-
mintAddress:
|
|
161
|
+
mintAddress: token.pubkey.toString()
|
|
142
162
|
});
|
|
143
163
|
|
|
144
164
|
const inputs = [firstInput, secondInput];
|
|
@@ -171,7 +191,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
171
191
|
}
|
|
172
192
|
// For real UTXOs, fetch the proof from API
|
|
173
193
|
const commitment = await utxo.getCommitment();
|
|
174
|
-
return fetchMerkleProof(commitment,
|
|
194
|
+
return fetchMerkleProof(commitment, token.name);
|
|
175
195
|
})
|
|
176
196
|
);
|
|
177
197
|
|
|
@@ -186,14 +206,14 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
186
206
|
amount: changeAmount.toString(),
|
|
187
207
|
keypair: utxoKeypairV2,
|
|
188
208
|
index: currentNextIndex,
|
|
189
|
-
mintAddress:
|
|
209
|
+
mintAddress: token.pubkey.toString()
|
|
190
210
|
}), // Change output
|
|
191
211
|
new Utxo({
|
|
192
212
|
lightWasm,
|
|
193
213
|
amount: '0',
|
|
194
214
|
keypair: utxoKeypairV2,
|
|
195
215
|
index: currentNextIndex + 1,
|
|
196
|
-
mintAddress:
|
|
216
|
+
mintAddress: token.pubkey.toString()
|
|
197
217
|
}) // Empty UTXO
|
|
198
218
|
];
|
|
199
219
|
|
|
@@ -255,7 +275,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
255
275
|
encryptedOutput2: encryptedOutput2,
|
|
256
276
|
fee: new BN(fee_base_units),
|
|
257
277
|
feeRecipient: feeRecipientTokenAccount,
|
|
258
|
-
mintAddress:
|
|
278
|
+
mintAddress: token.pubkey.toString()
|
|
259
279
|
};
|
|
260
280
|
// Calculate the extDataHash with the encrypted outputs
|
|
261
281
|
const calculatedExtDataHash = getExtDataHash(extData);
|
|
@@ -264,7 +284,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
264
284
|
const input = {
|
|
265
285
|
// Common transaction data
|
|
266
286
|
root: root,
|
|
267
|
-
mintAddress: getMintAddressField(
|
|
287
|
+
mintAddress: getMintAddressField(token.pubkey),// new mint address
|
|
268
288
|
publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
|
|
269
289
|
extDataHash: calculatedExtDataHash,
|
|
270
290
|
|
|
@@ -322,7 +342,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
322
342
|
[Buffer.from("global_config")],
|
|
323
343
|
PROGRAM_ID
|
|
324
344
|
);
|
|
325
|
-
const treeAta = getAssociatedTokenAddressSync(
|
|
345
|
+
const treeAta = getAssociatedTokenAddressSync(token.pubkey, globalConfigPda, true);
|
|
326
346
|
|
|
327
347
|
// Prepare withdraw parameters for indexer backend
|
|
328
348
|
const withdrawParams = {
|
|
@@ -342,7 +362,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
342
362
|
senderAddress: publicKey.toString(),
|
|
343
363
|
treeAta: treeAta.toString(),
|
|
344
364
|
recipientAta: recipient_ata.toString(),
|
|
345
|
-
mintAddress:
|
|
365
|
+
mintAddress: token.pubkey.toString(),
|
|
346
366
|
feeRecipientTokenAccount: feeRecipientTokenAccount.toString()
|
|
347
367
|
};
|
|
348
368
|
|
|
@@ -362,7 +382,7 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
362
382
|
console.log(`retryTimes: ${retryTimes}`)
|
|
363
383
|
await new Promise(resolve => setTimeout(resolve, itv * 1000));
|
|
364
384
|
console.log('Fetching updated tree state...');
|
|
365
|
-
let res = await fetch(RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=
|
|
385
|
+
let res = await fetch(RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=' + token.name)
|
|
366
386
|
let resJson = await res.json()
|
|
367
387
|
console.log('resJson:', resJson)
|
|
368
388
|
if (resJson.exists) {
|