@velumdotcash/sdk 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/__tests__/paylink.test.d.ts +9 -0
- package/dist/__tests__/paylink.test.js +254 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +12 -0
- package/dist/deposit.d.ts +22 -0
- package/dist/deposit.js +445 -0
- package/dist/depositSPL.d.ts +24 -0
- package/dist/depositSPL.js +499 -0
- package/dist/errors.d.ts +78 -0
- package/dist/errors.js +127 -0
- package/dist/exportUtils.d.ts +10 -0
- package/dist/exportUtils.js +10 -0
- package/dist/getUtxos.d.ts +30 -0
- package/dist/getUtxos.js +335 -0
- package/dist/getUtxosSPL.d.ts +34 -0
- package/dist/getUtxosSPL.js +442 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +436 -0
- package/dist/models/keypair.d.ts +26 -0
- package/dist/models/keypair.js +43 -0
- package/dist/models/utxo.d.ts +51 -0
- package/dist/models/utxo.js +99 -0
- package/dist/test_paylink_logic.test.d.ts +1 -0
- package/dist/test_paylink_logic.test.js +114 -0
- package/dist/utils/address_lookup_table.d.ts +9 -0
- package/dist/utils/address_lookup_table.js +45 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.js +56 -0
- package/dist/utils/debug-logger.d.ts +250 -0
- package/dist/utils/debug-logger.js +688 -0
- package/dist/utils/encryption.d.ts +152 -0
- package/dist/utils/encryption.js +700 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/merkle_tree.d.ts +92 -0
- package/dist/utils/merkle_tree.js +186 -0
- package/dist/utils/node-shim.d.ts +14 -0
- package/dist/utils/node-shim.js +21 -0
- package/dist/utils/prover.d.ts +36 -0
- package/dist/utils/prover.js +169 -0
- package/dist/utils/utils.d.ts +64 -0
- package/dist/utils/utils.js +165 -0
- package/dist/withdraw.d.ts +22 -0
- package/dist/withdraw.js +290 -0
- package/dist/withdrawSPL.d.ts +24 -0
- package/dist/withdrawSPL.js +329 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, } from "@solana/web3.js";
|
|
2
|
+
import { deposit } from "./deposit.js";
|
|
3
|
+
import { getBalanceFromUtxos, getUtxos, localstorageKey } from "./getUtxos.js";
|
|
4
|
+
import { getBalanceFromUtxosSPL, getUtxosSPL } from "./getUtxosSPL.js";
|
|
5
|
+
// Re-export error classes for consumers
|
|
6
|
+
export { PrivacyCashError, ZKProofError, NetworkError, InsufficientBalanceError, DepositLimitError, TransactionTimeoutError, } from "./errors.js";
|
|
7
|
+
// Re-export debug logging utilities for consumers
|
|
8
|
+
export { enableDebugLogging, disableDebugLogging, isDebugEnabled, setDebugLogger, enableVerboseLogging, disableVerboseLogging, isVerboseEnabled, } from "./utils/debug-logger.js";
|
|
9
|
+
import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, tokens, USDC_MINT, } from "./utils/constants.js";
|
|
10
|
+
import { logger, setLogger } from "./utils/logger.js";
|
|
11
|
+
import { EncryptionService } from "./utils/encryption.js";
|
|
12
|
+
import { enableDebugLogging, disableDebugLogging, isDebugEnabled, debugLogger, } from "./utils/debug-logger.js";
|
|
13
|
+
import { WasmFactory } from "@lightprotocol/hasher.rs";
|
|
14
|
+
import bs58 from "bs58";
|
|
15
|
+
import { withdraw } from "./withdraw.js";
|
|
16
|
+
import { depositSPL } from "./depositSPL.js";
|
|
17
|
+
import { withdrawSPL } from "./withdrawSPL.js";
|
|
18
|
+
import { getAssociatedTokenAddress } from "@solana/spl-token";
|
|
19
|
+
import { Keypair as UtxoKeypair } from "./models/keypair.js";
|
|
20
|
+
export class PrivacyCash {
|
|
21
|
+
connection;
|
|
22
|
+
publicKey;
|
|
23
|
+
encryptionService;
|
|
24
|
+
keypair;
|
|
25
|
+
transactionSigner;
|
|
26
|
+
isRuning = false;
|
|
27
|
+
status = "";
|
|
28
|
+
storage;
|
|
29
|
+
circuitPath;
|
|
30
|
+
debugMode = false;
|
|
31
|
+
constructor({ RPC_url, owner, publicKey, signature, transactionSigner, enableDebug, debugLogger: customDebugLogger, storage, circuitPath, }) {
|
|
32
|
+
this.connection = new Connection(RPC_url, "confirmed");
|
|
33
|
+
this.encryptionService = new EncryptionService();
|
|
34
|
+
// Configure debug logging for V3 decryption diagnostics
|
|
35
|
+
if (customDebugLogger === true) {
|
|
36
|
+
enableDebugLogging();
|
|
37
|
+
this.debugMode = true;
|
|
38
|
+
}
|
|
39
|
+
else if (typeof customDebugLogger === 'function') {
|
|
40
|
+
enableDebugLogging(customDebugLogger);
|
|
41
|
+
this.debugMode = true;
|
|
42
|
+
}
|
|
43
|
+
else if (isDebugEnabled()) {
|
|
44
|
+
// Environment variable is already set
|
|
45
|
+
this.debugMode = true;
|
|
46
|
+
}
|
|
47
|
+
if (owner) {
|
|
48
|
+
let keypair = getSolanaKeypair(owner);
|
|
49
|
+
if (!keypair) {
|
|
50
|
+
throw new Error('param "owner" is not a valid Private Key or Keypair');
|
|
51
|
+
}
|
|
52
|
+
this.keypair = keypair;
|
|
53
|
+
this.publicKey = keypair.publicKey;
|
|
54
|
+
this.encryptionService.deriveEncryptionKeyFromWallet(this.keypair);
|
|
55
|
+
}
|
|
56
|
+
else if (publicKey && signature) {
|
|
57
|
+
this.publicKey = publicKey;
|
|
58
|
+
this.encryptionService.deriveEncryptionKeyFromSignature(signature);
|
|
59
|
+
this.transactionSigner = transactionSigner;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new Error('Either "owner" (Keypair) or "publicKey" + "signature" must be provided');
|
|
63
|
+
}
|
|
64
|
+
if (storage) {
|
|
65
|
+
this.storage = storage;
|
|
66
|
+
}
|
|
67
|
+
else if (typeof window !== "undefined" && window.localStorage) {
|
|
68
|
+
this.storage = window.localStorage;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new Error("Storage implementation must be provided in non-browser environments");
|
|
72
|
+
}
|
|
73
|
+
this.circuitPath = circuitPath || "/circuit";
|
|
74
|
+
if (!enableDebug) {
|
|
75
|
+
this.startStatusRender();
|
|
76
|
+
this.setLogger((level, message) => {
|
|
77
|
+
if (level == "info") {
|
|
78
|
+
this.status = message;
|
|
79
|
+
}
|
|
80
|
+
else if (level == "error") {
|
|
81
|
+
console.log("error message: ", message);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Enable debug logging for V3 decryption diagnostics.
|
|
88
|
+
* Useful for troubleshooting paylink balance issues.
|
|
89
|
+
* @param customLogger Optional custom logger function
|
|
90
|
+
*/
|
|
91
|
+
enableDecryptionDebug(customLogger) {
|
|
92
|
+
enableDebugLogging(customLogger);
|
|
93
|
+
this.debugMode = true;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Disable debug logging
|
|
98
|
+
*/
|
|
99
|
+
disableDecryptionDebug() {
|
|
100
|
+
disableDebugLogging();
|
|
101
|
+
this.debugMode = false;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if debug logging is currently enabled
|
|
106
|
+
*/
|
|
107
|
+
isDecryptionDebugEnabled() {
|
|
108
|
+
return this.debugMode || isDebugEnabled();
|
|
109
|
+
}
|
|
110
|
+
setLogger(loger) {
|
|
111
|
+
setLogger(loger);
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
getSigner() {
|
|
115
|
+
if (this.transactionSigner) {
|
|
116
|
+
return this.transactionSigner;
|
|
117
|
+
}
|
|
118
|
+
return async (tx) => {
|
|
119
|
+
if (!this.keypair)
|
|
120
|
+
throw new Error("No signer available");
|
|
121
|
+
tx.sign([this.keypair]);
|
|
122
|
+
return tx;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Clears the cache of utxos.
|
|
127
|
+
*
|
|
128
|
+
* By default, downloaded utxos will be cached in the local storage. Thus the next time when you makes another
|
|
129
|
+
* deposit or withdraw or getPrivateBalance, the SDK only fetches the utxos that are not in the cache.
|
|
130
|
+
*
|
|
131
|
+
* This method clears the cache of utxos.
|
|
132
|
+
*/
|
|
133
|
+
async clearCache() {
|
|
134
|
+
if (!this.publicKey) {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
this.storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(this.publicKey));
|
|
138
|
+
this.storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(this.publicKey));
|
|
139
|
+
// spl
|
|
140
|
+
for (let token of tokens) {
|
|
141
|
+
let ata = await getAssociatedTokenAddress(token.pubkey, this.publicKey);
|
|
142
|
+
this.storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(ata));
|
|
143
|
+
this.storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(ata));
|
|
144
|
+
}
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Deposit SOL to the Privacy Cash.
|
|
149
|
+
*
|
|
150
|
+
* Lamports is the amount of SOL in lamports. e.g. if you want to deposit 0.01 SOL (10000000 lamports), call deposit({ lamports: 10000000 })
|
|
151
|
+
*/
|
|
152
|
+
async deposit({ lamports, recipientUtxoPublicKey, recipientEncryptionKey, }) {
|
|
153
|
+
this.isRuning = true;
|
|
154
|
+
logger.info("start depositting");
|
|
155
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
156
|
+
const transactionSigner = this.getSigner();
|
|
157
|
+
let res = await deposit({
|
|
158
|
+
lightWasm,
|
|
159
|
+
amount_in_lamports: lamports,
|
|
160
|
+
connection: this.connection,
|
|
161
|
+
encryptionService: this.encryptionService,
|
|
162
|
+
publicKey: this.publicKey,
|
|
163
|
+
transactionSigner,
|
|
164
|
+
keyBasePath: this.circuitPath,
|
|
165
|
+
storage: this.storage,
|
|
166
|
+
recipientUtxoPublicKey,
|
|
167
|
+
recipientEncryptionKey,
|
|
168
|
+
});
|
|
169
|
+
this.isRuning = false;
|
|
170
|
+
return res;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Deposit USDC to the Privacy Cash.
|
|
174
|
+
*/
|
|
175
|
+
async depositUSDC({ base_units, recipientUtxoPublicKey, recipientEncryptionKey, }) {
|
|
176
|
+
this.isRuning = true;
|
|
177
|
+
logger.info("start depositting");
|
|
178
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
179
|
+
const transactionSigner = this.getSigner();
|
|
180
|
+
let res = await depositSPL({
|
|
181
|
+
mintAddress: USDC_MINT,
|
|
182
|
+
lightWasm,
|
|
183
|
+
base_units: base_units,
|
|
184
|
+
connection: this.connection,
|
|
185
|
+
encryptionService: this.encryptionService,
|
|
186
|
+
publicKey: this.publicKey,
|
|
187
|
+
transactionSigner,
|
|
188
|
+
keyBasePath: this.circuitPath,
|
|
189
|
+
storage: this.storage,
|
|
190
|
+
recipientUtxoPublicKey,
|
|
191
|
+
recipientEncryptionKey,
|
|
192
|
+
});
|
|
193
|
+
this.isRuning = false;
|
|
194
|
+
return res;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Withdraw SOL from the Privacy Cash.
|
|
198
|
+
*
|
|
199
|
+
* Lamports is the amount of SOL in lamports. e.g. if you want to withdraw 0.01 SOL (10000000 lamports), call withdraw({ lamports: 10000000 })
|
|
200
|
+
*/
|
|
201
|
+
async withdraw({ lamports, recipientAddress, referrer, }) {
|
|
202
|
+
this.isRuning = true;
|
|
203
|
+
logger.info("start withdrawing");
|
|
204
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
205
|
+
let recipient = recipientAddress
|
|
206
|
+
? new PublicKey(recipientAddress)
|
|
207
|
+
: this.publicKey;
|
|
208
|
+
let res = await withdraw({
|
|
209
|
+
lightWasm,
|
|
210
|
+
amount_in_lamports: lamports,
|
|
211
|
+
connection: this.connection,
|
|
212
|
+
encryptionService: this.encryptionService,
|
|
213
|
+
publicKey: this.publicKey,
|
|
214
|
+
recipient,
|
|
215
|
+
keyBasePath: this.circuitPath,
|
|
216
|
+
storage: this.storage,
|
|
217
|
+
referrer,
|
|
218
|
+
});
|
|
219
|
+
logger.debug(`Withdraw successful. Recipient ${recipient} received ${res.amount_in_lamports / LAMPORTS_PER_SOL} SOL, with ${res.fee_in_lamports / LAMPORTS_PER_SOL} SOL relayers fees`);
|
|
220
|
+
this.isRuning = false;
|
|
221
|
+
return res;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Withdraw USDC from the Privacy Cash.
|
|
225
|
+
*
|
|
226
|
+
* base_units is the amount of USDC in base unit. e.g. if you want to withdraw 1 USDC (1,000,000 base unit), call withdraw({ base_units: 1000000, recipientAddress: 'some_address' })
|
|
227
|
+
*/
|
|
228
|
+
async withdrawUSDC({ base_units, recipientAddress, referrer, }) {
|
|
229
|
+
this.isRuning = true;
|
|
230
|
+
logger.info("start withdrawing");
|
|
231
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
232
|
+
let recipient = recipientAddress
|
|
233
|
+
? new PublicKey(recipientAddress)
|
|
234
|
+
: this.publicKey;
|
|
235
|
+
let res = await withdrawSPL({
|
|
236
|
+
mintAddress: USDC_MINT,
|
|
237
|
+
lightWasm,
|
|
238
|
+
base_units,
|
|
239
|
+
connection: this.connection,
|
|
240
|
+
encryptionService: this.encryptionService,
|
|
241
|
+
publicKey: this.publicKey,
|
|
242
|
+
recipient,
|
|
243
|
+
keyBasePath: this.circuitPath,
|
|
244
|
+
storage: this.storage,
|
|
245
|
+
referrer,
|
|
246
|
+
});
|
|
247
|
+
logger.debug(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`);
|
|
248
|
+
this.isRuning = false;
|
|
249
|
+
return res;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Returns the amount of lamports current wallet has in Privacy Cash.
|
|
253
|
+
* Also tracks and summarizes any decryption failures for debugging.
|
|
254
|
+
*/
|
|
255
|
+
async getPrivateBalance(abortSignal) {
|
|
256
|
+
logger.info("getting private balance");
|
|
257
|
+
this.isRuning = true;
|
|
258
|
+
// Start failure tracking for this balance fetch
|
|
259
|
+
debugLogger.startFailureTracking();
|
|
260
|
+
let utxos = await getUtxos({
|
|
261
|
+
publicKey: this.publicKey,
|
|
262
|
+
connection: this.connection,
|
|
263
|
+
encryptionService: this.encryptionService,
|
|
264
|
+
storage: this.storage,
|
|
265
|
+
abortSignal,
|
|
266
|
+
});
|
|
267
|
+
// End failure tracking and get summary
|
|
268
|
+
const failureSummary = debugLogger.endFailureTracking();
|
|
269
|
+
this.isRuning = false;
|
|
270
|
+
const balance = getBalanceFromUtxos(utxos);
|
|
271
|
+
return { ...balance, failureSummary };
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Returns the amount of base units current wallet has in Privacy Cash.
|
|
275
|
+
* Also tracks and summarizes any decryption failures for debugging.
|
|
276
|
+
*/
|
|
277
|
+
async getPrivateBalanceUSDC() {
|
|
278
|
+
logger.info("getting private balance");
|
|
279
|
+
this.isRuning = true;
|
|
280
|
+
// Start failure tracking for this balance fetch
|
|
281
|
+
debugLogger.startFailureTracking();
|
|
282
|
+
let utxos = await getUtxosSPL({
|
|
283
|
+
publicKey: this.publicKey,
|
|
284
|
+
connection: this.connection,
|
|
285
|
+
encryptionService: this.encryptionService,
|
|
286
|
+
storage: this.storage,
|
|
287
|
+
mintAddress: USDC_MINT,
|
|
288
|
+
});
|
|
289
|
+
// End failure tracking and get summary
|
|
290
|
+
const failureSummary = debugLogger.endFailureTracking();
|
|
291
|
+
this.isRuning = false;
|
|
292
|
+
const balance = getBalanceFromUtxosSPL(utxos);
|
|
293
|
+
return { ...balance, failureSummary };
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Returns the amount of base units current wallet has in Privacy Cash.
|
|
297
|
+
* Also tracks and summarizes any decryption failures for debugging.
|
|
298
|
+
*/
|
|
299
|
+
async getPrivateBalanceSpl(mintAddress) {
|
|
300
|
+
logger.info("getting private balance for SPL token");
|
|
301
|
+
this.isRuning = true;
|
|
302
|
+
// Start failure tracking for this balance fetch
|
|
303
|
+
debugLogger.startFailureTracking();
|
|
304
|
+
let utxos = await getUtxosSPL({
|
|
305
|
+
publicKey: this.publicKey,
|
|
306
|
+
connection: this.connection,
|
|
307
|
+
encryptionService: this.encryptionService,
|
|
308
|
+
storage: this.storage,
|
|
309
|
+
mintAddress,
|
|
310
|
+
});
|
|
311
|
+
// End failure tracking and get summary
|
|
312
|
+
const failureSummary = debugLogger.endFailureTracking();
|
|
313
|
+
this.isRuning = false;
|
|
314
|
+
const balance = getBalanceFromUtxosSPL(utxos);
|
|
315
|
+
return { ...balance, failureSummary };
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Returns true if the code is running in a browser.
|
|
319
|
+
*/
|
|
320
|
+
isBrowser() {
|
|
321
|
+
return typeof window !== "undefined";
|
|
322
|
+
}
|
|
323
|
+
async startStatusRender() {
|
|
324
|
+
let frames = ["-", "\\", "|", "/"];
|
|
325
|
+
let i = 0;
|
|
326
|
+
while (true) {
|
|
327
|
+
if (this.isRuning) {
|
|
328
|
+
let k = i % frames.length;
|
|
329
|
+
i++;
|
|
330
|
+
stdWrite(this.status, frames[k]);
|
|
331
|
+
}
|
|
332
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Deposit SPL to the Privacy Cash.
|
|
337
|
+
*/
|
|
338
|
+
async depositSPL({ base_units, mintAddress, amount, recipientUtxoPublicKey, recipientEncryptionKey, }) {
|
|
339
|
+
this.isRuning = true;
|
|
340
|
+
logger.info("start depositting");
|
|
341
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
342
|
+
const transactionSigner = this.getSigner();
|
|
343
|
+
let res = await depositSPL({
|
|
344
|
+
lightWasm,
|
|
345
|
+
base_units,
|
|
346
|
+
amount,
|
|
347
|
+
connection: this.connection,
|
|
348
|
+
encryptionService: this.encryptionService,
|
|
349
|
+
publicKey: this.publicKey,
|
|
350
|
+
transactionSigner,
|
|
351
|
+
keyBasePath: this.circuitPath,
|
|
352
|
+
storage: this.storage,
|
|
353
|
+
mintAddress,
|
|
354
|
+
recipientUtxoPublicKey,
|
|
355
|
+
recipientEncryptionKey,
|
|
356
|
+
});
|
|
357
|
+
this.isRuning = false;
|
|
358
|
+
return res;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Withdraw SPL from the Privacy Cash.
|
|
362
|
+
*/
|
|
363
|
+
async withdrawSPL({ base_units, mintAddress, recipientAddress, amount, referrer, }) {
|
|
364
|
+
this.isRuning = true;
|
|
365
|
+
logger.info("start withdrawing");
|
|
366
|
+
let lightWasm = await WasmFactory.getInstance();
|
|
367
|
+
let recipient = recipientAddress
|
|
368
|
+
? new PublicKey(recipientAddress)
|
|
369
|
+
: this.publicKey;
|
|
370
|
+
let res = await withdrawSPL({
|
|
371
|
+
lightWasm,
|
|
372
|
+
base_units,
|
|
373
|
+
amount,
|
|
374
|
+
connection: this.connection,
|
|
375
|
+
encryptionService: this.encryptionService,
|
|
376
|
+
publicKey: this.publicKey,
|
|
377
|
+
recipient,
|
|
378
|
+
keyBasePath: this.circuitPath,
|
|
379
|
+
storage: this.storage,
|
|
380
|
+
mintAddress,
|
|
381
|
+
referrer,
|
|
382
|
+
});
|
|
383
|
+
logger.debug(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`);
|
|
384
|
+
this.isRuning = false;
|
|
385
|
+
return res;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Returns the asymmetric encryption public key (X25519) for receiving encrypted paylink data.
|
|
389
|
+
* This key is used by senders to encrypt UTXO data that only this wallet can decrypt.
|
|
390
|
+
*/
|
|
391
|
+
getAsymmetricPublicKey() {
|
|
392
|
+
return this.encryptionService.getAsymmetricPublicKey();
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Returns the shielded public key (BN254 curve point) for receiving private payments.
|
|
396
|
+
* This is the UTXO ownership key derived from the wallet signature.
|
|
397
|
+
*/
|
|
398
|
+
async getShieldedPublicKey() {
|
|
399
|
+
const lightWasm = await WasmFactory.getInstance();
|
|
400
|
+
const privateKeyHex = this.encryptionService.getUtxoPrivateKeyV2();
|
|
401
|
+
const keypair = new UtxoKeypair(privateKeyHex, lightWasm);
|
|
402
|
+
return keypair.pubkey.toString();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function getSolanaKeypair(secret) {
|
|
406
|
+
try {
|
|
407
|
+
if (secret instanceof Keypair) {
|
|
408
|
+
return secret;
|
|
409
|
+
}
|
|
410
|
+
let keyArray;
|
|
411
|
+
if (typeof secret === "string") {
|
|
412
|
+
keyArray = bs58.decode(secret);
|
|
413
|
+
}
|
|
414
|
+
else if (secret instanceof Uint8Array) {
|
|
415
|
+
keyArray = secret;
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
// number[]
|
|
419
|
+
keyArray = Uint8Array.from(secret);
|
|
420
|
+
}
|
|
421
|
+
if (keyArray.length !== 32 && keyArray.length !== 64) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return Keypair.fromSecretKey(keyArray);
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function stdWrite(status, frame) {
|
|
431
|
+
if (typeof process !== "undefined" && process.stdout?.write) {
|
|
432
|
+
let blue = "\x1b[34m";
|
|
433
|
+
let reset = "\x1b[0m";
|
|
434
|
+
process.stdout.write(`${frame}status: ${blue}${status}${reset}\r`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keypair module for ZK Cash
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographic keypair functionality for the ZK Cash system
|
|
5
|
+
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
|
+
*/
|
|
7
|
+
import BN from 'bn.js';
|
|
8
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
9
|
+
/**
|
|
10
|
+
* Simplified version of Keypair
|
|
11
|
+
*/
|
|
12
|
+
export declare class Keypair {
|
|
13
|
+
privkey: BN;
|
|
14
|
+
pubkey: BN;
|
|
15
|
+
private lightWasm;
|
|
16
|
+
constructor(privkeyHex: string, lightWasm: hasher.LightWasm);
|
|
17
|
+
/**
|
|
18
|
+
* Sign a message using keypair private key
|
|
19
|
+
*
|
|
20
|
+
* @param {string|number|BigNumber} commitment a hex string with commitment
|
|
21
|
+
* @param {string|number|BigNumber} merklePath a hex string with merkle path
|
|
22
|
+
* @returns {BigNumber} a hex string with signature
|
|
23
|
+
*/
|
|
24
|
+
sign(commitment: string, merklePath: string): string;
|
|
25
|
+
static generateNew(lightWasm: hasher.LightWasm): Promise<Keypair>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keypair module for ZK Cash
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographic keypair functionality for the ZK Cash system
|
|
5
|
+
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
|
+
*/
|
|
7
|
+
import BN from 'bn.js';
|
|
8
|
+
import { ethers } from 'ethers';
|
|
9
|
+
// Field size constant
|
|
10
|
+
const FIELD_SIZE = new BN('21888242871839275222246405745257275088548364400416034343698204186575808495617');
|
|
11
|
+
/**
|
|
12
|
+
* Simplified version of Keypair
|
|
13
|
+
*/
|
|
14
|
+
export class Keypair {
|
|
15
|
+
privkey;
|
|
16
|
+
pubkey;
|
|
17
|
+
lightWasm;
|
|
18
|
+
constructor(privkeyHex, lightWasm) {
|
|
19
|
+
const rawDecimal = BigInt(privkeyHex);
|
|
20
|
+
this.privkey = new BN((rawDecimal % BigInt(FIELD_SIZE.toString())).toString());
|
|
21
|
+
this.lightWasm = lightWasm;
|
|
22
|
+
// TODO: lazily compute pubkey
|
|
23
|
+
this.pubkey = new BN(this.lightWasm.poseidonHashString([this.privkey.toString()]));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Sign a message using keypair private key
|
|
27
|
+
*
|
|
28
|
+
* @param {string|number|BigNumber} commitment a hex string with commitment
|
|
29
|
+
* @param {string|number|BigNumber} merklePath a hex string with merkle path
|
|
30
|
+
* @returns {BigNumber} a hex string with signature
|
|
31
|
+
*/
|
|
32
|
+
sign(commitment, merklePath) {
|
|
33
|
+
return this.lightWasm.poseidonHashString([this.privkey.toString(), commitment, merklePath]);
|
|
34
|
+
}
|
|
35
|
+
static async generateNew(lightWasm) {
|
|
36
|
+
// Tornado Cash Nova uses ethers.js to generate a random private key
|
|
37
|
+
// We can't generate Solana keypairs because it won't fit in the field size
|
|
38
|
+
// It's OK to use ethereum secret keys, because the secret key is only used for the proof generation.
|
|
39
|
+
// Namely, it's used to guarantee the uniqueness of the nullifier.
|
|
40
|
+
const wallet = ethers.Wallet.createRandom();
|
|
41
|
+
return new Keypair(wallet.privateKey, lightWasm);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UTXO (Unspent Transaction Output) module for ZK Cash
|
|
3
|
+
*
|
|
4
|
+
* Provides UTXO functionality for the ZK Cash system
|
|
5
|
+
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
|
+
*/
|
|
7
|
+
import BN from 'bn.js';
|
|
8
|
+
import { Keypair } from './keypair.js';
|
|
9
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
10
|
+
/**
|
|
11
|
+
* Simplified Utxo class inspired by Tornado Cash Nova
|
|
12
|
+
* Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
|
|
13
|
+
*/
|
|
14
|
+
export declare class Utxo {
|
|
15
|
+
amount: BN;
|
|
16
|
+
blinding: BN;
|
|
17
|
+
keypair?: Keypair;
|
|
18
|
+
pubkey: BN;
|
|
19
|
+
index: number;
|
|
20
|
+
mintAddress: string;
|
|
21
|
+
version: 'v1' | 'v2';
|
|
22
|
+
private lightWasm;
|
|
23
|
+
constructor({ lightWasm, amount,
|
|
24
|
+
/**
|
|
25
|
+
* Tornado nova doesn't use solana eddsa with curve 25519 but their own "keypair"
|
|
26
|
+
* which is:
|
|
27
|
+
* - private key: random [31;u8]
|
|
28
|
+
* - public key: PoseidonHash(privateKey)
|
|
29
|
+
*
|
|
30
|
+
* Generate a new keypair for each UTXO
|
|
31
|
+
*/
|
|
32
|
+
keypair, publicKey, blinding, // Use fixed value for consistency instead of randomBN()
|
|
33
|
+
index, mintAddress, // Default to Solana native SOL mint address,
|
|
34
|
+
version }: {
|
|
35
|
+
lightWasm: hasher.LightWasm;
|
|
36
|
+
amount?: BN | number | string;
|
|
37
|
+
keypair?: Keypair;
|
|
38
|
+
publicKey?: BN | string;
|
|
39
|
+
blinding?: BN | number | string;
|
|
40
|
+
index?: number;
|
|
41
|
+
mintAddress?: string;
|
|
42
|
+
version?: 'v1' | 'v2';
|
|
43
|
+
});
|
|
44
|
+
getCommitment(): Promise<string>;
|
|
45
|
+
getNullifier(): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Log all the UTXO's public properties and derived values in JSON format
|
|
48
|
+
* @returns Promise that resolves once all logging is complete
|
|
49
|
+
*/
|
|
50
|
+
log(): Promise<void>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UTXO (Unspent Transaction Output) module for ZK Cash
|
|
3
|
+
*
|
|
4
|
+
* Provides UTXO functionality for the ZK Cash system
|
|
5
|
+
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
|
+
*/
|
|
7
|
+
import BN from 'bn.js';
|
|
8
|
+
import { Keypair } from './keypair.js';
|
|
9
|
+
import { ethers } from 'ethers';
|
|
10
|
+
import { getMintAddressField } from '../utils/utils.js';
|
|
11
|
+
import { PublicKey } from '@solana/web3.js';
|
|
12
|
+
/**
|
|
13
|
+
* Simplified Utxo class inspired by Tornado Cash Nova
|
|
14
|
+
* Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
|
|
15
|
+
*/
|
|
16
|
+
export class Utxo {
|
|
17
|
+
amount;
|
|
18
|
+
blinding;
|
|
19
|
+
keypair;
|
|
20
|
+
pubkey;
|
|
21
|
+
index;
|
|
22
|
+
mintAddress;
|
|
23
|
+
version;
|
|
24
|
+
lightWasm;
|
|
25
|
+
constructor({ lightWasm, amount = new BN(0),
|
|
26
|
+
/**
|
|
27
|
+
* Tornado nova doesn't use solana eddsa with curve 25519 but their own "keypair"
|
|
28
|
+
* which is:
|
|
29
|
+
* - private key: random [31;u8]
|
|
30
|
+
* - public key: PoseidonHash(privateKey)
|
|
31
|
+
*
|
|
32
|
+
* Generate a new keypair for each UTXO
|
|
33
|
+
*/
|
|
34
|
+
keypair, publicKey, blinding = new BN(Math.floor(Math.random() * 1000000000)), // Use fixed value for consistency instead of randomBN()
|
|
35
|
+
index = 0, mintAddress = '11111111111111111111111111111112', // Default to Solana native SOL mint address,
|
|
36
|
+
version = 'v2' }) {
|
|
37
|
+
this.amount = new BN(amount.toString());
|
|
38
|
+
this.blinding = new BN(blinding.toString());
|
|
39
|
+
this.lightWasm = lightWasm;
|
|
40
|
+
if (keypair) {
|
|
41
|
+
this.keypair = keypair;
|
|
42
|
+
this.pubkey = keypair.pubkey;
|
|
43
|
+
}
|
|
44
|
+
else if (publicKey) {
|
|
45
|
+
this.pubkey = new BN(publicKey.toString());
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.keypair = new Keypair(ethers.Wallet.createRandom().privateKey, lightWasm);
|
|
49
|
+
this.pubkey = this.keypair.pubkey;
|
|
50
|
+
}
|
|
51
|
+
this.index = index;
|
|
52
|
+
this.mintAddress = mintAddress;
|
|
53
|
+
this.version = version;
|
|
54
|
+
}
|
|
55
|
+
async getCommitment() {
|
|
56
|
+
// return this.lightWasm.poseidonHashString([this.amount.toString(), this.keypair.pubkey.toString(), this.blinding.toString(), this.mintAddress]);
|
|
57
|
+
const mintAddressField = getMintAddressField(new PublicKey(this.mintAddress));
|
|
58
|
+
return this.lightWasm.poseidonHashString([
|
|
59
|
+
this.amount.toString(),
|
|
60
|
+
this.pubkey.toString(),
|
|
61
|
+
this.blinding.toString(),
|
|
62
|
+
mintAddressField
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
async getNullifier() {
|
|
66
|
+
if (!this.keypair) {
|
|
67
|
+
throw new Error('Cannot generate nullifier without private key');
|
|
68
|
+
}
|
|
69
|
+
const commitmentValue = await this.getCommitment();
|
|
70
|
+
const signature = this.keypair.sign(commitmentValue, new BN(this.index).toString());
|
|
71
|
+
return this.lightWasm.poseidonHashString([commitmentValue, new BN(this.index).toString(), signature]);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Log all the UTXO's public properties and derived values in JSON format
|
|
75
|
+
* @returns Promise that resolves once all logging is complete
|
|
76
|
+
*/
|
|
77
|
+
async log() {
|
|
78
|
+
// Prepare the UTXO data object
|
|
79
|
+
const utxoData = {
|
|
80
|
+
amount: this.amount.toString(),
|
|
81
|
+
blinding: this.blinding.toString(),
|
|
82
|
+
index: this.index,
|
|
83
|
+
mintAddress: this.mintAddress,
|
|
84
|
+
keypair: {
|
|
85
|
+
pubkey: this.pubkey.toString()
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// Add derived values
|
|
89
|
+
try {
|
|
90
|
+
utxoData.commitment = await this.getCommitment();
|
|
91
|
+
utxoData.nullifier = await this.getNullifier();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
utxoData.error = error.message;
|
|
95
|
+
}
|
|
96
|
+
// Output as formatted JSON
|
|
97
|
+
console.log(JSON.stringify(utxoData, null, 2));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|