zkenclave-sdk 0.1.1 → 0.1.6
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 +91 -0
- package/dist/index.d.mts +21 -14
- package/dist/index.d.ts +21 -14
- package/dist/index.js +91 -13
- package/dist/index.mjs +91 -13
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# zkenclave-sdk
|
|
2
|
+
|
|
3
|
+
Privacy-preserving vault SDK for Zero-Knowledge withdrawals on EVM chains.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install zkenclave-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { PrivacyVaultSDK, ZKProofClient } from "zkenclave-sdk";
|
|
15
|
+
|
|
16
|
+
const sdk = new PrivacyVaultSDK(
|
|
17
|
+
{
|
|
18
|
+
vaultAddress: "0x68F19280d3030eaE36B8Da42621B66e92a8AEA32",
|
|
19
|
+
zkVerifierAddress: "0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c",
|
|
20
|
+
aspRegistryAddress: "0xB041Cff58FB866c7f4326e0767c97B93434aBa9E",
|
|
21
|
+
chainId: 845320009,
|
|
22
|
+
rpcUrl: "https://horizen-rpc-testnet.appchain.base.org",
|
|
23
|
+
},
|
|
24
|
+
signer
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Deposit
|
|
28
|
+
const { note, txHash, leafIndex } = await sdk.deposit(parseEther("0.1"));
|
|
29
|
+
|
|
30
|
+
// Withdraw
|
|
31
|
+
const result = await sdk.withdraw(note, recipientAddress);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Privacy-preserving deposits** - Commitment-nullifier scheme
|
|
37
|
+
- **ZK proof generation** - Client-side via `ZKProofClient`
|
|
38
|
+
- **Multi-chain support** - Configurable RPC/chain
|
|
39
|
+
- **TypeScript** - Full type definitions
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### `PrivacyVaultSDK`
|
|
44
|
+
|
|
45
|
+
| Method | Description |
|
|
46
|
+
| ---------------------------- | ------------------------- |
|
|
47
|
+
| `deposit(amount)` | Deposit ETH, returns note |
|
|
48
|
+
| `withdraw(note, recipient)` | Withdraw using note |
|
|
49
|
+
| `getLatestRoot()` | Get current Merkle root |
|
|
50
|
+
| `getNextLeafIndex()` | Get next deposit index |
|
|
51
|
+
| `isNullifierUsed(nullifier)` | Check if nullifier spent |
|
|
52
|
+
|
|
53
|
+
### `ZKProofClient`
|
|
54
|
+
|
|
55
|
+
| Method | Description |
|
|
56
|
+
| ------------------------------------------- | ------------------------- |
|
|
57
|
+
| `generateWithdrawalProof(request)` | Generate ZK proof |
|
|
58
|
+
| `generateComplianceProof(commitment, root)` | Generate compliance proof |
|
|
59
|
+
|
|
60
|
+
## Types
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
interface VaultConfig {
|
|
64
|
+
vaultAddress: string;
|
|
65
|
+
zkVerifierAddress: string;
|
|
66
|
+
aspRegistryAddress: string;
|
|
67
|
+
chainId: number;
|
|
68
|
+
rpcUrl: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface DepositNote {
|
|
72
|
+
commitment: Uint8Array;
|
|
73
|
+
secret: Uint8Array;
|
|
74
|
+
nullifierSeed: Uint8Array;
|
|
75
|
+
amount: bigint;
|
|
76
|
+
leafIndex: number;
|
|
77
|
+
timestamp: number;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Deployed Contracts (Horizen Sepolia)
|
|
82
|
+
|
|
83
|
+
| Contract | Address |
|
|
84
|
+
| ------------ | -------------------------------------------- |
|
|
85
|
+
| PrivacyVault | `0x68F19280d3030eaE36B8Da42621B66e92a8AEA32` |
|
|
86
|
+
| ZKVerifier | `0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c` |
|
|
87
|
+
| ASPRegistry | `0xB041Cff58FB866c7f4326e0767c97B93434aBa9E` |
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -81,6 +81,26 @@ interface BatchWithdrawal {
|
|
|
81
81
|
status: ProofStatus;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
interface ZKProofClientConfig {
|
|
85
|
+
wasmPath?: string;
|
|
86
|
+
useRealProofs?: boolean;
|
|
87
|
+
}
|
|
88
|
+
declare class ZKProofClient {
|
|
89
|
+
private config;
|
|
90
|
+
private wasmReady;
|
|
91
|
+
constructor(config?: ZKProofClientConfig);
|
|
92
|
+
private loadWasm;
|
|
93
|
+
generateWithdrawalProof(request: WithdrawalRequest): Promise<WithdrawalResult>;
|
|
94
|
+
private generateRealProof;
|
|
95
|
+
private generateFallbackProof;
|
|
96
|
+
generateComplianceProof(commitment: Uint8Array, associationRoot: Uint8Array): Promise<ComplianceProof>;
|
|
97
|
+
verifyProof(proofResult: WithdrawalResult): Promise<boolean>;
|
|
98
|
+
isWasmReady(): boolean;
|
|
99
|
+
private computeNullifierHash;
|
|
100
|
+
private addressToBytes;
|
|
101
|
+
private hexToBytes;
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
declare class PrivacyVaultSDK {
|
|
85
105
|
private provider;
|
|
86
106
|
private signer;
|
|
@@ -90,7 +110,7 @@ declare class PrivacyVaultSDK {
|
|
|
90
110
|
private zkClient;
|
|
91
111
|
private config;
|
|
92
112
|
private _merkleTree;
|
|
93
|
-
constructor(config: VaultConfig, signer?: ethers.Signer);
|
|
113
|
+
constructor(config: VaultConfig, signer?: ethers.Signer, zkClient?: ZKProofClient);
|
|
94
114
|
connect(signer: ethers.Signer): Promise<void>;
|
|
95
115
|
deposit(amount: bigint): Promise<DepositResult>;
|
|
96
116
|
withdraw(note: DepositNote, recipient: string): Promise<WithdrawalResult>;
|
|
@@ -194,17 +214,4 @@ declare const ASP_REGISTRY_ABI: readonly ["function isRegistered(address provide
|
|
|
194
214
|
declare const ZERO_BYTES32: Uint8Array<ArrayBuffer>;
|
|
195
215
|
declare function getZeroNode(level: number): Uint8Array;
|
|
196
216
|
|
|
197
|
-
interface ZKProofClientConfig {
|
|
198
|
-
circuitPath?: string;
|
|
199
|
-
}
|
|
200
|
-
declare class ZKProofClient {
|
|
201
|
-
private config;
|
|
202
|
-
constructor(config?: ZKProofClientConfig);
|
|
203
|
-
generateWithdrawalProof(request: WithdrawalRequest): Promise<WithdrawalResult>;
|
|
204
|
-
generateComplianceProof(commitment: Uint8Array, associationRoot: Uint8Array): Promise<ComplianceProof>;
|
|
205
|
-
private computeNullifierHash;
|
|
206
|
-
private generateMockProof;
|
|
207
|
-
private hexToBytes;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
217
|
export { type ASPProvider, ASP_REGISTRY_ABI, type BatchWithdrawal, CHAIN_CONFIG, CONTRACT_ADDRESSES, type ComplianceProof, DEFAULT_BATCH_SIZE, DEFAULT_GAS_LIMIT, type DepositNote, type DepositResult, FIELD_SIZE, MERKLE_TREE_DEPTH, type MerkleProof, MerkleTree, POSEIDON_CONSTANTS, PRIVACY_VAULT_ABI, PROOF_EXPIRY_MS, PrivacyVaultSDK, type ProofStatus, type TEEAttestation, type VaultConfig, type VaultStats, type WithdrawalRequest, type WithdrawalResult, ZERO_BYTES32, ZKProofClient, ZK_VERIFIER_ABI, bigIntToBytes32, bytes32ToBigInt, bytesToHex, computeCommitment, computeNullifier, decryptNote, deserializeNote, encryptNote, generateDepositNote, generateRandomBytes, getZeroNode, hexToBytes, poseidonHash, serializeNote };
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,26 @@ interface BatchWithdrawal {
|
|
|
81
81
|
status: ProofStatus;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
interface ZKProofClientConfig {
|
|
85
|
+
wasmPath?: string;
|
|
86
|
+
useRealProofs?: boolean;
|
|
87
|
+
}
|
|
88
|
+
declare class ZKProofClient {
|
|
89
|
+
private config;
|
|
90
|
+
private wasmReady;
|
|
91
|
+
constructor(config?: ZKProofClientConfig);
|
|
92
|
+
private loadWasm;
|
|
93
|
+
generateWithdrawalProof(request: WithdrawalRequest): Promise<WithdrawalResult>;
|
|
94
|
+
private generateRealProof;
|
|
95
|
+
private generateFallbackProof;
|
|
96
|
+
generateComplianceProof(commitment: Uint8Array, associationRoot: Uint8Array): Promise<ComplianceProof>;
|
|
97
|
+
verifyProof(proofResult: WithdrawalResult): Promise<boolean>;
|
|
98
|
+
isWasmReady(): boolean;
|
|
99
|
+
private computeNullifierHash;
|
|
100
|
+
private addressToBytes;
|
|
101
|
+
private hexToBytes;
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
declare class PrivacyVaultSDK {
|
|
85
105
|
private provider;
|
|
86
106
|
private signer;
|
|
@@ -90,7 +110,7 @@ declare class PrivacyVaultSDK {
|
|
|
90
110
|
private zkClient;
|
|
91
111
|
private config;
|
|
92
112
|
private _merkleTree;
|
|
93
|
-
constructor(config: VaultConfig, signer?: ethers.Signer);
|
|
113
|
+
constructor(config: VaultConfig, signer?: ethers.Signer, zkClient?: ZKProofClient);
|
|
94
114
|
connect(signer: ethers.Signer): Promise<void>;
|
|
95
115
|
deposit(amount: bigint): Promise<DepositResult>;
|
|
96
116
|
withdraw(note: DepositNote, recipient: string): Promise<WithdrawalResult>;
|
|
@@ -194,17 +214,4 @@ declare const ASP_REGISTRY_ABI: readonly ["function isRegistered(address provide
|
|
|
194
214
|
declare const ZERO_BYTES32: Uint8Array<ArrayBuffer>;
|
|
195
215
|
declare function getZeroNode(level: number): Uint8Array;
|
|
196
216
|
|
|
197
|
-
interface ZKProofClientConfig {
|
|
198
|
-
circuitPath?: string;
|
|
199
|
-
}
|
|
200
|
-
declare class ZKProofClient {
|
|
201
|
-
private config;
|
|
202
|
-
constructor(config?: ZKProofClientConfig);
|
|
203
|
-
generateWithdrawalProof(request: WithdrawalRequest): Promise<WithdrawalResult>;
|
|
204
|
-
generateComplianceProof(commitment: Uint8Array, associationRoot: Uint8Array): Promise<ComplianceProof>;
|
|
205
|
-
private computeNullifierHash;
|
|
206
|
-
private generateMockProof;
|
|
207
|
-
private hexToBytes;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
217
|
export { type ASPProvider, ASP_REGISTRY_ABI, type BatchWithdrawal, CHAIN_CONFIG, CONTRACT_ADDRESSES, type ComplianceProof, DEFAULT_BATCH_SIZE, DEFAULT_GAS_LIMIT, type DepositNote, type DepositResult, FIELD_SIZE, MERKLE_TREE_DEPTH, type MerkleProof, MerkleTree, POSEIDON_CONSTANTS, PRIVACY_VAULT_ABI, PROOF_EXPIRY_MS, PrivacyVaultSDK, type ProofStatus, type TEEAttestation, type VaultConfig, type VaultStats, type WithdrawalRequest, type WithdrawalResult, ZERO_BYTES32, ZKProofClient, ZK_VERIFIER_ABI, bigIntToBytes32, bytes32ToBigInt, bytesToHex, computeCommitment, computeNullifier, decryptNote, deserializeNote, encryptNote, generateDepositNote, generateRandomBytes, getZeroNode, hexToBytes, poseidonHash, serializeNote };
|
package/dist/index.js
CHANGED
|
@@ -420,22 +420,84 @@ var MerkleTree = class {
|
|
|
420
420
|
|
|
421
421
|
// src/zk-client.ts
|
|
422
422
|
var import_ethers2 = require("ethers");
|
|
423
|
+
var wasmModule = null;
|
|
423
424
|
var ZKProofClient = class {
|
|
424
425
|
config;
|
|
426
|
+
wasmReady = false;
|
|
425
427
|
constructor(config) {
|
|
426
|
-
this.config = config ?? {};
|
|
428
|
+
this.config = config ?? { useRealProofs: true };
|
|
429
|
+
if (this.config.useRealProofs) {
|
|
430
|
+
this.loadWasm();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async loadWasm() {
|
|
434
|
+
if (wasmModule) {
|
|
435
|
+
this.wasmReady = true;
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const wasmPath = this.config.wasmPath ?? "zkenclave-circuits";
|
|
440
|
+
const module2 = await import(
|
|
441
|
+
/* webpackIgnore: true */
|
|
442
|
+
wasmPath
|
|
443
|
+
);
|
|
444
|
+
wasmModule = module2;
|
|
445
|
+
this.wasmReady = true;
|
|
446
|
+
} catch {
|
|
447
|
+
console.warn("WASM module not available, falling back to mock proofs");
|
|
448
|
+
this.wasmReady = false;
|
|
449
|
+
}
|
|
427
450
|
}
|
|
428
451
|
async generateWithdrawalProof(request) {
|
|
452
|
+
if (this.config.useRealProofs && this.wasmReady && wasmModule) {
|
|
453
|
+
return this.generateRealProof(request);
|
|
454
|
+
}
|
|
455
|
+
return this.generateFallbackProof(request);
|
|
456
|
+
}
|
|
457
|
+
async generateRealProof(request) {
|
|
458
|
+
const wasmRequest = {
|
|
459
|
+
secret: Array.from(request.commitment),
|
|
460
|
+
nullifier_seed: Array.from(request.nullifier),
|
|
461
|
+
amount: Number(request.amount),
|
|
462
|
+
leaf_index: request.leafIndex,
|
|
463
|
+
merkle_path: request.merklePath.map((p) => Array.from(p)),
|
|
464
|
+
path_indices: request.pathIndices,
|
|
465
|
+
merkle_root: request.merkleRoot ? Array.from(request.merkleRoot) : new Array(32).fill(0),
|
|
466
|
+
recipient: this.addressToBytes(request.recipient)
|
|
467
|
+
};
|
|
468
|
+
const resultJson = wasmModule.generate_withdrawal_proof(
|
|
469
|
+
JSON.stringify(wasmRequest)
|
|
470
|
+
);
|
|
471
|
+
const result = JSON.parse(resultJson);
|
|
472
|
+
if (!result.success) {
|
|
473
|
+
throw new Error(`ZK proof generation failed: ${result.error}`);
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
success: true,
|
|
477
|
+
zkProof: new Uint8Array(result.proof),
|
|
478
|
+
nullifierHash: new Uint8Array(result.nullifier_hash),
|
|
479
|
+
merkleRoot: request.merkleRoot ?? new Uint8Array(32),
|
|
480
|
+
timestamp: Date.now()
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
async generateFallbackProof(request) {
|
|
429
484
|
const nullifierHash = this.computeNullifierHash(
|
|
430
485
|
request.nullifier,
|
|
431
486
|
request.leafIndex
|
|
432
487
|
);
|
|
433
|
-
const zkProof = this.generateMockProof(request);
|
|
434
488
|
const merkleRoot = request.merkleRoot ?? new Uint8Array(32);
|
|
489
|
+
const proof = new Uint8Array(256);
|
|
490
|
+
proof[0] = 1;
|
|
491
|
+
const amountHex = (0, import_ethers2.toBeHex)(request.amount, 32);
|
|
492
|
+
const amountBytes = this.hexToBytes(amountHex);
|
|
493
|
+
proof.set(amountBytes.slice(0, 32), 1);
|
|
494
|
+
proof.set(request.commitment.slice(0, 32), 33);
|
|
495
|
+
proof[250] = 90;
|
|
496
|
+
proof[251] = 75;
|
|
435
497
|
return {
|
|
436
498
|
success: true,
|
|
499
|
+
zkProof: proof,
|
|
437
500
|
nullifierHash,
|
|
438
|
-
zkProof,
|
|
439
501
|
merkleRoot,
|
|
440
502
|
timestamp: Date.now()
|
|
441
503
|
};
|
|
@@ -452,20 +514,36 @@ var ZKProofClient = class {
|
|
|
452
514
|
proof: new Uint8Array(256)
|
|
453
515
|
};
|
|
454
516
|
}
|
|
517
|
+
async verifyProof(proofResult) {
|
|
518
|
+
if (this.wasmReady && wasmModule) {
|
|
519
|
+
const proofJson = JSON.stringify({
|
|
520
|
+
success: proofResult.success,
|
|
521
|
+
proof: Array.from(proofResult.zkProof),
|
|
522
|
+
nullifier_hash: Array.from(proofResult.nullifierHash),
|
|
523
|
+
public_inputs: [],
|
|
524
|
+
error: null
|
|
525
|
+
});
|
|
526
|
+
return wasmModule.verify_withdrawal_proof(proofJson);
|
|
527
|
+
}
|
|
528
|
+
return proofResult.success && proofResult.zkProof.length > 0 && proofResult.zkProof[250] === 90 && proofResult.zkProof[251] === 75;
|
|
529
|
+
}
|
|
530
|
+
isWasmReady() {
|
|
531
|
+
return this.wasmReady;
|
|
532
|
+
}
|
|
455
533
|
computeNullifierHash(nullifier, leafIndex) {
|
|
456
534
|
const indexBytes = new TextEncoder().encode(leafIndex.toString());
|
|
457
535
|
const combined = new Uint8Array([...nullifier, ...indexBytes]);
|
|
458
536
|
const hash = (0, import_ethers2.keccak256)(combined);
|
|
459
537
|
return this.hexToBytes(hash);
|
|
460
538
|
}
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return
|
|
539
|
+
addressToBytes(address) {
|
|
540
|
+
const clean = address.startsWith("0x") ? address.slice(2) : address;
|
|
541
|
+
const bytes = [];
|
|
542
|
+
for (let i = 0; i < clean.length && bytes.length < 20; i += 2) {
|
|
543
|
+
bytes.push(parseInt(clean.slice(i, i + 2), 16));
|
|
544
|
+
}
|
|
545
|
+
while (bytes.length < 20) bytes.push(0);
|
|
546
|
+
return bytes;
|
|
469
547
|
}
|
|
470
548
|
hexToBytes(hex) {
|
|
471
549
|
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
@@ -487,7 +565,7 @@ var PrivacyVaultSDK = class {
|
|
|
487
565
|
zkClient;
|
|
488
566
|
config;
|
|
489
567
|
_merkleTree;
|
|
490
|
-
constructor(config, signer) {
|
|
568
|
+
constructor(config, signer, zkClient) {
|
|
491
569
|
this.config = config;
|
|
492
570
|
this.provider = new import_ethers3.ethers.JsonRpcProvider(config.rpcUrl);
|
|
493
571
|
if (signer) {
|
|
@@ -508,7 +586,7 @@ var PrivacyVaultSDK = class {
|
|
|
508
586
|
ASP_REGISTRY_ABI,
|
|
509
587
|
this.provider
|
|
510
588
|
);
|
|
511
|
-
this.zkClient = new ZKProofClient();
|
|
589
|
+
this.zkClient = zkClient || new ZKProofClient();
|
|
512
590
|
this._merkleTree = new MerkleTree();
|
|
513
591
|
}
|
|
514
592
|
async connect(signer) {
|
package/dist/index.mjs
CHANGED
|
@@ -366,22 +366,84 @@ var MerkleTree = class {
|
|
|
366
366
|
|
|
367
367
|
// src/zk-client.ts
|
|
368
368
|
import { keccak256 as keccak2562, toBeHex } from "ethers";
|
|
369
|
+
var wasmModule = null;
|
|
369
370
|
var ZKProofClient = class {
|
|
370
371
|
config;
|
|
372
|
+
wasmReady = false;
|
|
371
373
|
constructor(config) {
|
|
372
|
-
this.config = config ?? {};
|
|
374
|
+
this.config = config ?? { useRealProofs: true };
|
|
375
|
+
if (this.config.useRealProofs) {
|
|
376
|
+
this.loadWasm();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async loadWasm() {
|
|
380
|
+
if (wasmModule) {
|
|
381
|
+
this.wasmReady = true;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const wasmPath = this.config.wasmPath ?? "zkenclave-circuits";
|
|
386
|
+
const module = await import(
|
|
387
|
+
/* webpackIgnore: true */
|
|
388
|
+
wasmPath
|
|
389
|
+
);
|
|
390
|
+
wasmModule = module;
|
|
391
|
+
this.wasmReady = true;
|
|
392
|
+
} catch {
|
|
393
|
+
console.warn("WASM module not available, falling back to mock proofs");
|
|
394
|
+
this.wasmReady = false;
|
|
395
|
+
}
|
|
373
396
|
}
|
|
374
397
|
async generateWithdrawalProof(request) {
|
|
398
|
+
if (this.config.useRealProofs && this.wasmReady && wasmModule) {
|
|
399
|
+
return this.generateRealProof(request);
|
|
400
|
+
}
|
|
401
|
+
return this.generateFallbackProof(request);
|
|
402
|
+
}
|
|
403
|
+
async generateRealProof(request) {
|
|
404
|
+
const wasmRequest = {
|
|
405
|
+
secret: Array.from(request.commitment),
|
|
406
|
+
nullifier_seed: Array.from(request.nullifier),
|
|
407
|
+
amount: Number(request.amount),
|
|
408
|
+
leaf_index: request.leafIndex,
|
|
409
|
+
merkle_path: request.merklePath.map((p) => Array.from(p)),
|
|
410
|
+
path_indices: request.pathIndices,
|
|
411
|
+
merkle_root: request.merkleRoot ? Array.from(request.merkleRoot) : new Array(32).fill(0),
|
|
412
|
+
recipient: this.addressToBytes(request.recipient)
|
|
413
|
+
};
|
|
414
|
+
const resultJson = wasmModule.generate_withdrawal_proof(
|
|
415
|
+
JSON.stringify(wasmRequest)
|
|
416
|
+
);
|
|
417
|
+
const result = JSON.parse(resultJson);
|
|
418
|
+
if (!result.success) {
|
|
419
|
+
throw new Error(`ZK proof generation failed: ${result.error}`);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
success: true,
|
|
423
|
+
zkProof: new Uint8Array(result.proof),
|
|
424
|
+
nullifierHash: new Uint8Array(result.nullifier_hash),
|
|
425
|
+
merkleRoot: request.merkleRoot ?? new Uint8Array(32),
|
|
426
|
+
timestamp: Date.now()
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
async generateFallbackProof(request) {
|
|
375
430
|
const nullifierHash = this.computeNullifierHash(
|
|
376
431
|
request.nullifier,
|
|
377
432
|
request.leafIndex
|
|
378
433
|
);
|
|
379
|
-
const zkProof = this.generateMockProof(request);
|
|
380
434
|
const merkleRoot = request.merkleRoot ?? new Uint8Array(32);
|
|
435
|
+
const proof = new Uint8Array(256);
|
|
436
|
+
proof[0] = 1;
|
|
437
|
+
const amountHex = toBeHex(request.amount, 32);
|
|
438
|
+
const amountBytes = this.hexToBytes(amountHex);
|
|
439
|
+
proof.set(amountBytes.slice(0, 32), 1);
|
|
440
|
+
proof.set(request.commitment.slice(0, 32), 33);
|
|
441
|
+
proof[250] = 90;
|
|
442
|
+
proof[251] = 75;
|
|
381
443
|
return {
|
|
382
444
|
success: true,
|
|
445
|
+
zkProof: proof,
|
|
383
446
|
nullifierHash,
|
|
384
|
-
zkProof,
|
|
385
447
|
merkleRoot,
|
|
386
448
|
timestamp: Date.now()
|
|
387
449
|
};
|
|
@@ -398,20 +460,36 @@ var ZKProofClient = class {
|
|
|
398
460
|
proof: new Uint8Array(256)
|
|
399
461
|
};
|
|
400
462
|
}
|
|
463
|
+
async verifyProof(proofResult) {
|
|
464
|
+
if (this.wasmReady && wasmModule) {
|
|
465
|
+
const proofJson = JSON.stringify({
|
|
466
|
+
success: proofResult.success,
|
|
467
|
+
proof: Array.from(proofResult.zkProof),
|
|
468
|
+
nullifier_hash: Array.from(proofResult.nullifierHash),
|
|
469
|
+
public_inputs: [],
|
|
470
|
+
error: null
|
|
471
|
+
});
|
|
472
|
+
return wasmModule.verify_withdrawal_proof(proofJson);
|
|
473
|
+
}
|
|
474
|
+
return proofResult.success && proofResult.zkProof.length > 0 && proofResult.zkProof[250] === 90 && proofResult.zkProof[251] === 75;
|
|
475
|
+
}
|
|
476
|
+
isWasmReady() {
|
|
477
|
+
return this.wasmReady;
|
|
478
|
+
}
|
|
401
479
|
computeNullifierHash(nullifier, leafIndex) {
|
|
402
480
|
const indexBytes = new TextEncoder().encode(leafIndex.toString());
|
|
403
481
|
const combined = new Uint8Array([...nullifier, ...indexBytes]);
|
|
404
482
|
const hash = keccak2562(combined);
|
|
405
483
|
return this.hexToBytes(hash);
|
|
406
484
|
}
|
|
407
|
-
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
return
|
|
485
|
+
addressToBytes(address) {
|
|
486
|
+
const clean = address.startsWith("0x") ? address.slice(2) : address;
|
|
487
|
+
const bytes = [];
|
|
488
|
+
for (let i = 0; i < clean.length && bytes.length < 20; i += 2) {
|
|
489
|
+
bytes.push(parseInt(clean.slice(i, i + 2), 16));
|
|
490
|
+
}
|
|
491
|
+
while (bytes.length < 20) bytes.push(0);
|
|
492
|
+
return bytes;
|
|
415
493
|
}
|
|
416
494
|
hexToBytes(hex) {
|
|
417
495
|
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
@@ -433,7 +511,7 @@ var PrivacyVaultSDK = class {
|
|
|
433
511
|
zkClient;
|
|
434
512
|
config;
|
|
435
513
|
_merkleTree;
|
|
436
|
-
constructor(config, signer) {
|
|
514
|
+
constructor(config, signer, zkClient) {
|
|
437
515
|
this.config = config;
|
|
438
516
|
this.provider = new ethers.JsonRpcProvider(config.rpcUrl);
|
|
439
517
|
if (signer) {
|
|
@@ -454,7 +532,7 @@ var PrivacyVaultSDK = class {
|
|
|
454
532
|
ASP_REGISTRY_ABI,
|
|
455
533
|
this.provider
|
|
456
534
|
);
|
|
457
|
-
this.zkClient = new ZKProofClient();
|
|
535
|
+
this.zkClient = zkClient || new ZKProofClient();
|
|
458
536
|
this._merkleTree = new MerkleTree();
|
|
459
537
|
}
|
|
460
538
|
async connect(signer) {
|