zkenclave-sdk 0.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/index.d.mts +209 -0
- package/dist/index.d.ts +209 -0
- package/dist/index.js +652 -0
- package/dist/index.mjs +597 -0
- package/package.json +54 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ASP_REGISTRY_ABI: () => ASP_REGISTRY_ABI,
|
|
24
|
+
CHAIN_CONFIG: () => CHAIN_CONFIG,
|
|
25
|
+
CONTRACT_ADDRESSES: () => CONTRACT_ADDRESSES,
|
|
26
|
+
DEFAULT_BATCH_SIZE: () => DEFAULT_BATCH_SIZE,
|
|
27
|
+
DEFAULT_GAS_LIMIT: () => DEFAULT_GAS_LIMIT,
|
|
28
|
+
FIELD_SIZE: () => FIELD_SIZE,
|
|
29
|
+
MERKLE_TREE_DEPTH: () => MERKLE_TREE_DEPTH,
|
|
30
|
+
MerkleTree: () => MerkleTree,
|
|
31
|
+
POSEIDON_CONSTANTS: () => POSEIDON_CONSTANTS,
|
|
32
|
+
PRIVACY_VAULT_ABI: () => PRIVACY_VAULT_ABI,
|
|
33
|
+
PROOF_EXPIRY_MS: () => PROOF_EXPIRY_MS,
|
|
34
|
+
PrivacyVaultSDK: () => PrivacyVaultSDK,
|
|
35
|
+
ZERO_BYTES32: () => ZERO_BYTES32,
|
|
36
|
+
ZKProofClient: () => ZKProofClient,
|
|
37
|
+
ZK_VERIFIER_ABI: () => ZK_VERIFIER_ABI,
|
|
38
|
+
bigIntToBytes32: () => bigIntToBytes32,
|
|
39
|
+
bytes32ToBigInt: () => bytes32ToBigInt,
|
|
40
|
+
bytesToHex: () => bytesToHex,
|
|
41
|
+
computeCommitment: () => computeCommitment,
|
|
42
|
+
computeNullifier: () => computeNullifier,
|
|
43
|
+
decryptNote: () => decryptNote,
|
|
44
|
+
deserializeNote: () => deserializeNote,
|
|
45
|
+
encryptNote: () => encryptNote,
|
|
46
|
+
generateDepositNote: () => generateDepositNote,
|
|
47
|
+
generateRandomBytes: () => generateRandomBytes,
|
|
48
|
+
getZeroNode: () => getZeroNode,
|
|
49
|
+
hexToBytes: () => hexToBytes,
|
|
50
|
+
poseidonHash: () => poseidonHash,
|
|
51
|
+
serializeNote: () => serializeNote
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/vault.ts
|
|
56
|
+
var import_ethers3 = require("ethers");
|
|
57
|
+
|
|
58
|
+
// src/constants.ts
|
|
59
|
+
var import_ethers = require("ethers");
|
|
60
|
+
var MERKLE_TREE_DEPTH = 20;
|
|
61
|
+
var FIELD_SIZE = BigInt(
|
|
62
|
+
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
|
|
63
|
+
);
|
|
64
|
+
var DEFAULT_GAS_LIMIT = 500000n;
|
|
65
|
+
var DEFAULT_BATCH_SIZE = 10;
|
|
66
|
+
var PROOF_EXPIRY_MS = 3e5;
|
|
67
|
+
var CONTRACT_ADDRESSES = {
|
|
68
|
+
ethereum: {
|
|
69
|
+
mainnet: {
|
|
70
|
+
vault: "0x0000000000000000000000000000000000000000",
|
|
71
|
+
zkVerifier: "0x0000000000000000000000000000000000000000",
|
|
72
|
+
aspRegistry: "0x0000000000000000000000000000000000000000"
|
|
73
|
+
},
|
|
74
|
+
celoSepolia: {
|
|
75
|
+
vault: "0x68F19280d3030eaE36B8Da42621B66e92a8AEA32",
|
|
76
|
+
zkVerifier: "0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c",
|
|
77
|
+
aspRegistry: "0xB041Cff58FB866c7f4326e0767c97B93434aBa9E"
|
|
78
|
+
},
|
|
79
|
+
horizenSepolia: {
|
|
80
|
+
vault: "0x68F19280d3030eaE36B8Da42621B66e92a8AEA32",
|
|
81
|
+
zkVerifier: "0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c",
|
|
82
|
+
aspRegistry: "0xB041Cff58FB866c7f4326e0767c97B93434aBa9E"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
phala: {
|
|
86
|
+
mainnet: {
|
|
87
|
+
phatContract: ""
|
|
88
|
+
},
|
|
89
|
+
testnet: {
|
|
90
|
+
phatContract: ""
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var CHAIN_CONFIG = {
|
|
95
|
+
1: {
|
|
96
|
+
name: "Ethereum Mainnet",
|
|
97
|
+
rpcUrl: "https://eth.llamarpc.com",
|
|
98
|
+
explorer: "https://etherscan.io"
|
|
99
|
+
},
|
|
100
|
+
11142220: {
|
|
101
|
+
name: "Celo Sepolia",
|
|
102
|
+
rpcUrl: "https://forno.celo-sepolia.celo-testnet.org",
|
|
103
|
+
explorer: "https://sepolia.celoscan.io"
|
|
104
|
+
},
|
|
105
|
+
2035: {
|
|
106
|
+
name: "Phala L2",
|
|
107
|
+
rpcUrl: "https://rpc.phala.network",
|
|
108
|
+
explorer: "https://explorer.phala.network"
|
|
109
|
+
},
|
|
110
|
+
845320009: {
|
|
111
|
+
name: "Horizen Sepolia Testnet",
|
|
112
|
+
rpcUrl: "https://horizen-rpc-testnet.appchain.base.org",
|
|
113
|
+
explorer: "https://explorer-horizen-testnet.appchain.base.org"
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var POSEIDON_CONSTANTS = {
|
|
117
|
+
T: 3,
|
|
118
|
+
ROUNDS_F: 8,
|
|
119
|
+
ROUNDS_P: 57
|
|
120
|
+
};
|
|
121
|
+
var PRIVACY_VAULT_ABI = [
|
|
122
|
+
"function deposit(bytes32 commitment) external payable",
|
|
123
|
+
"function withdraw(bytes32 nullifierHash, bytes32 root, address recipient, uint256 amount, bytes calldata zkProof, bytes calldata teeAttestation) external",
|
|
124
|
+
"function withdrawWithCompliance(bytes32 nullifierHash, bytes32 root, address recipient, uint256 amount, bytes calldata zkProof, bytes calldata associationProof, address aspProvider) external",
|
|
125
|
+
"function isKnownRoot(bytes32 root) external view returns (bool)",
|
|
126
|
+
"function getLatestRoot() external view returns (bytes32)",
|
|
127
|
+
"function getNextLeafIndex() external view returns (uint256)",
|
|
128
|
+
"function isNullifierUsed(bytes32 nullifier) external view returns (bool)",
|
|
129
|
+
"function getUserDeposits(address user) external view returns (bytes32[])",
|
|
130
|
+
"event Deposit(bytes32 indexed commitment, uint256 leafIndex, uint256 amount, uint256 timestamp)",
|
|
131
|
+
"event Withdrawal(bytes32 indexed nullifierHash, address indexed recipient, uint256 amount, bytes32 merkleRoot)"
|
|
132
|
+
];
|
|
133
|
+
var ZK_VERIFIER_ABI = [
|
|
134
|
+
"function verifyProof(bytes calldata proof, bytes32[] calldata publicInputs) external returns (bool)",
|
|
135
|
+
"function verifyProofView(bytes calldata proof, bytes32[] calldata publicInputs) external view returns (bool)",
|
|
136
|
+
"function isProofVerified(bytes32 proofHash) external view returns (bool)"
|
|
137
|
+
];
|
|
138
|
+
var ASP_REGISTRY_ABI = [
|
|
139
|
+
"function isRegistered(address provider) external view returns (bool)",
|
|
140
|
+
"function getProviderRoot(address provider) external view returns (bytes32)",
|
|
141
|
+
"function getActiveProviders() external view returns (address[])",
|
|
142
|
+
"function getHighReputationProviders(uint256 minScore) external view returns (address[])"
|
|
143
|
+
];
|
|
144
|
+
var ZERO_BYTES32 = new Uint8Array(32);
|
|
145
|
+
function getZeroNode(level) {
|
|
146
|
+
const zeros = [ZERO_BYTES32];
|
|
147
|
+
for (let i = 1; i <= level; i++) {
|
|
148
|
+
const prev = zeros[i - 1];
|
|
149
|
+
const combined = new Uint8Array(64);
|
|
150
|
+
combined.set(prev, 0);
|
|
151
|
+
combined.set(prev, 32);
|
|
152
|
+
zeros.push(hashKeccak256(combined));
|
|
153
|
+
}
|
|
154
|
+
return zeros[level];
|
|
155
|
+
}
|
|
156
|
+
function hashKeccak256(data) {
|
|
157
|
+
return (0, import_ethers.getBytes)((0, import_ethers.keccak256)(data));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/crypto.ts
|
|
161
|
+
function generateRandomBytes(length) {
|
|
162
|
+
const buffer = new Uint8Array(length);
|
|
163
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
164
|
+
crypto.getRandomValues(buffer);
|
|
165
|
+
} else {
|
|
166
|
+
for (let i = 0; i < length; i++) {
|
|
167
|
+
buffer[i] = Math.floor(Math.random() * 256);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return buffer;
|
|
171
|
+
}
|
|
172
|
+
function bytesToHex(bytes) {
|
|
173
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
174
|
+
}
|
|
175
|
+
function hexToBytes(hex) {
|
|
176
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
177
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
178
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
179
|
+
bytes[i] = parseInt(cleanHex.substr(i * 2, 2), 16);
|
|
180
|
+
}
|
|
181
|
+
return bytes;
|
|
182
|
+
}
|
|
183
|
+
function bigIntToBytes32(value) {
|
|
184
|
+
const hex = value.toString(16).padStart(64, "0");
|
|
185
|
+
return hexToBytes(hex);
|
|
186
|
+
}
|
|
187
|
+
function bytes32ToBigInt(bytes) {
|
|
188
|
+
if (bytes.length !== 32) {
|
|
189
|
+
throw new Error("Invalid bytes32 length");
|
|
190
|
+
}
|
|
191
|
+
return BigInt(bytesToHex(bytes));
|
|
192
|
+
}
|
|
193
|
+
function poseidonHash(inputs) {
|
|
194
|
+
if (inputs.length > 2) {
|
|
195
|
+
throw new Error("Poseidon T3 supports max 2 inputs");
|
|
196
|
+
}
|
|
197
|
+
let state = inputs.map((i) => i % FIELD_SIZE);
|
|
198
|
+
while (state.length < 3) {
|
|
199
|
+
state.push(0n);
|
|
200
|
+
}
|
|
201
|
+
const C = [
|
|
202
|
+
0x0ee9a592ba9a9518d05986d656f40c2114c4993c11bb29938d21d47304cd8e6en,
|
|
203
|
+
0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864n,
|
|
204
|
+
0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5n
|
|
205
|
+
];
|
|
206
|
+
const M = [
|
|
207
|
+
[
|
|
208
|
+
0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118bn,
|
|
209
|
+
0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0n,
|
|
210
|
+
0x2b90bba00f05d28c6d4c9d2f1d4d3c2e7f5d8e4a3b2c1d0e9f8a7b6c5d4e3f2an
|
|
211
|
+
],
|
|
212
|
+
[
|
|
213
|
+
0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771n,
|
|
214
|
+
0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7n,
|
|
215
|
+
0x1e3f7a4c5d6b8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3fn
|
|
216
|
+
],
|
|
217
|
+
[
|
|
218
|
+
0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911n,
|
|
219
|
+
0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773c5e6f71c7c5e3an,
|
|
220
|
+
0x2b4129c2e5a87d9c3e1f5b7d9a2c4e6f8a0b2d4c6e8f0a2b4c6d8e0f2a4b6c8dn
|
|
221
|
+
]
|
|
222
|
+
];
|
|
223
|
+
const sbox = (x) => {
|
|
224
|
+
const x2 = x * x % FIELD_SIZE;
|
|
225
|
+
const x4 = x2 * x2 % FIELD_SIZE;
|
|
226
|
+
return x4 * x % FIELD_SIZE;
|
|
227
|
+
};
|
|
228
|
+
const mix = (s) => {
|
|
229
|
+
const result = [];
|
|
230
|
+
for (let i = 0; i < 3; i++) {
|
|
231
|
+
let sum = 0n;
|
|
232
|
+
for (let j = 0; j < 3; j++) {
|
|
233
|
+
sum = (sum + M[i][j] * s[j]) % FIELD_SIZE;
|
|
234
|
+
}
|
|
235
|
+
result.push(sum);
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
};
|
|
239
|
+
const halfF = POSEIDON_CONSTANTS.ROUNDS_F / 2;
|
|
240
|
+
for (let r = 0; r < halfF; r++) {
|
|
241
|
+
for (let i = 0; i < 3; i++) {
|
|
242
|
+
state[i] = (state[i] + C[i]) % FIELD_SIZE;
|
|
243
|
+
state[i] = sbox(state[i]);
|
|
244
|
+
}
|
|
245
|
+
state = mix(state);
|
|
246
|
+
}
|
|
247
|
+
for (let r = 0; r < POSEIDON_CONSTANTS.ROUNDS_P; r++) {
|
|
248
|
+
for (let i = 0; i < 3; i++) {
|
|
249
|
+
state[i] = (state[i] + C[i]) % FIELD_SIZE;
|
|
250
|
+
}
|
|
251
|
+
state[0] = sbox(state[0]);
|
|
252
|
+
state = mix(state);
|
|
253
|
+
}
|
|
254
|
+
for (let r = 0; r < halfF; r++) {
|
|
255
|
+
for (let i = 0; i < 3; i++) {
|
|
256
|
+
state[i] = (state[i] + C[i]) % FIELD_SIZE;
|
|
257
|
+
state[i] = sbox(state[i]);
|
|
258
|
+
}
|
|
259
|
+
state = mix(state);
|
|
260
|
+
}
|
|
261
|
+
return state[0];
|
|
262
|
+
}
|
|
263
|
+
function computeCommitment(secret, nullifierSeed, amount) {
|
|
264
|
+
const intermediate = poseidonHash([secret, nullifierSeed]);
|
|
265
|
+
return poseidonHash([intermediate, amount]);
|
|
266
|
+
}
|
|
267
|
+
function computeNullifier(nullifierSeed, leafIndex) {
|
|
268
|
+
return poseidonHash([nullifierSeed, BigInt(leafIndex)]);
|
|
269
|
+
}
|
|
270
|
+
function generateDepositNote(amount) {
|
|
271
|
+
const secret = generateRandomBytes(32);
|
|
272
|
+
const nullifierSeed = generateRandomBytes(32);
|
|
273
|
+
const secretBigInt = bytes32ToBigInt(secret);
|
|
274
|
+
const nullifierBigInt = bytes32ToBigInt(nullifierSeed);
|
|
275
|
+
const commitmentBigInt = computeCommitment(
|
|
276
|
+
secretBigInt,
|
|
277
|
+
nullifierBigInt,
|
|
278
|
+
amount
|
|
279
|
+
);
|
|
280
|
+
const commitment = bigIntToBytes32(commitmentBigInt);
|
|
281
|
+
return {
|
|
282
|
+
commitment,
|
|
283
|
+
secret,
|
|
284
|
+
nullifierSeed,
|
|
285
|
+
amount,
|
|
286
|
+
leafIndex: -1,
|
|
287
|
+
timestamp: Date.now()
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function serializeNote(note) {
|
|
291
|
+
const data = {
|
|
292
|
+
commitment: bytesToHex(note.commitment),
|
|
293
|
+
secret: bytesToHex(note.secret),
|
|
294
|
+
nullifierSeed: bytesToHex(note.nullifierSeed),
|
|
295
|
+
amount: note.amount.toString(),
|
|
296
|
+
leafIndex: note.leafIndex,
|
|
297
|
+
timestamp: note.timestamp
|
|
298
|
+
};
|
|
299
|
+
return btoa(JSON.stringify(data));
|
|
300
|
+
}
|
|
301
|
+
function deserializeNote(serialized) {
|
|
302
|
+
const data = JSON.parse(atob(serialized));
|
|
303
|
+
return {
|
|
304
|
+
commitment: hexToBytes(data.commitment),
|
|
305
|
+
secret: hexToBytes(data.secret),
|
|
306
|
+
nullifierSeed: hexToBytes(data.nullifierSeed),
|
|
307
|
+
amount: BigInt(data.amount),
|
|
308
|
+
leafIndex: data.leafIndex,
|
|
309
|
+
timestamp: data.timestamp
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function encryptNote(note, password) {
|
|
313
|
+
const serialized = serializeNote(note);
|
|
314
|
+
const encrypted = xorEncrypt(serialized, password);
|
|
315
|
+
return bytesToHex(encrypted);
|
|
316
|
+
}
|
|
317
|
+
function decryptNote(encrypted, password) {
|
|
318
|
+
const bytes = hexToBytes(encrypted);
|
|
319
|
+
const decrypted = xorDecrypt(bytes, password);
|
|
320
|
+
return deserializeNote(decrypted);
|
|
321
|
+
}
|
|
322
|
+
function xorEncrypt(data, key) {
|
|
323
|
+
const dataBytes = new TextEncoder().encode(data);
|
|
324
|
+
const keyBytes = new TextEncoder().encode(key);
|
|
325
|
+
const result = new Uint8Array(dataBytes.length);
|
|
326
|
+
for (let i = 0; i < dataBytes.length; i++) {
|
|
327
|
+
result[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
function xorDecrypt(data, key) {
|
|
332
|
+
const keyBytes = new TextEncoder().encode(key);
|
|
333
|
+
const result = new Uint8Array(data.length);
|
|
334
|
+
for (let i = 0; i < data.length; i++) {
|
|
335
|
+
result[i] = data[i] ^ keyBytes[i % keyBytes.length];
|
|
336
|
+
}
|
|
337
|
+
return new TextDecoder().decode(result);
|
|
338
|
+
}
|
|
339
|
+
var MerkleTree = class {
|
|
340
|
+
leaves;
|
|
341
|
+
layers;
|
|
342
|
+
depth;
|
|
343
|
+
zeroValues;
|
|
344
|
+
constructor(depth = MERKLE_TREE_DEPTH) {
|
|
345
|
+
this.depth = depth;
|
|
346
|
+
this.leaves = [];
|
|
347
|
+
this.layers = [];
|
|
348
|
+
this.zeroValues = this.computeZeroValues();
|
|
349
|
+
}
|
|
350
|
+
computeZeroValues() {
|
|
351
|
+
const zeros = [0n];
|
|
352
|
+
for (let i = 1; i <= this.depth; i++) {
|
|
353
|
+
zeros.push(poseidonHash([zeros[i - 1], zeros[i - 1]]));
|
|
354
|
+
}
|
|
355
|
+
return zeros;
|
|
356
|
+
}
|
|
357
|
+
insert(leaf) {
|
|
358
|
+
const index = this.leaves.length;
|
|
359
|
+
this.leaves.push(leaf);
|
|
360
|
+
this.rebuild();
|
|
361
|
+
return index;
|
|
362
|
+
}
|
|
363
|
+
rebuild() {
|
|
364
|
+
this.layers = [this.leaves.slice()];
|
|
365
|
+
const targetSize = 1 << this.depth;
|
|
366
|
+
while (this.layers[0].length < targetSize) {
|
|
367
|
+
this.layers[0].push(this.zeroValues[0]);
|
|
368
|
+
}
|
|
369
|
+
for (let level = 0; level < this.depth; level++) {
|
|
370
|
+
const currentLayer = this.layers[level];
|
|
371
|
+
const nextLayer = [];
|
|
372
|
+
for (let i = 0; i < currentLayer.length; i += 2) {
|
|
373
|
+
const left = currentLayer[i];
|
|
374
|
+
const right = currentLayer[i + 1] ?? this.zeroValues[level];
|
|
375
|
+
nextLayer.push(poseidonHash([left, right]));
|
|
376
|
+
}
|
|
377
|
+
this.layers.push(nextLayer);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
getRoot() {
|
|
381
|
+
if (this.layers.length === 0) {
|
|
382
|
+
return this.zeroValues[this.depth];
|
|
383
|
+
}
|
|
384
|
+
return this.layers[this.layers.length - 1][0];
|
|
385
|
+
}
|
|
386
|
+
generateProof(index) {
|
|
387
|
+
if (index >= this.leaves.length) {
|
|
388
|
+
throw new Error("Index out of bounds");
|
|
389
|
+
}
|
|
390
|
+
const path = [];
|
|
391
|
+
const indices = [];
|
|
392
|
+
let currentIndex = index;
|
|
393
|
+
for (let level = 0; level < this.depth; level++) {
|
|
394
|
+
const isRight = currentIndex % 2 === 1;
|
|
395
|
+
const siblingIndex = isRight ? currentIndex - 1 : currentIndex + 1;
|
|
396
|
+
const sibling = this.layers[level][siblingIndex] ?? this.zeroValues[level];
|
|
397
|
+
path.push(bigIntToBytes32(sibling));
|
|
398
|
+
indices.push(isRight);
|
|
399
|
+
currentIndex = Math.floor(currentIndex / 2);
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
path,
|
|
403
|
+
indices,
|
|
404
|
+
root: bigIntToBytes32(this.getRoot())
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
verifyProof(leaf, proof) {
|
|
408
|
+
let current = leaf;
|
|
409
|
+
for (let i = 0; i < proof.path.length; i++) {
|
|
410
|
+
const sibling = bytes32ToBigInt(proof.path[i]);
|
|
411
|
+
if (proof.indices[i]) {
|
|
412
|
+
current = poseidonHash([sibling, current]);
|
|
413
|
+
} else {
|
|
414
|
+
current = poseidonHash([current, sibling]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return current === bytes32ToBigInt(proof.root);
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/zk-client.ts
|
|
422
|
+
var import_ethers2 = require("ethers");
|
|
423
|
+
var ZKProofClient = class {
|
|
424
|
+
config;
|
|
425
|
+
constructor(config) {
|
|
426
|
+
this.config = config ?? {};
|
|
427
|
+
}
|
|
428
|
+
async generateWithdrawalProof(request) {
|
|
429
|
+
const nullifierHash = this.computeNullifierHash(
|
|
430
|
+
request.nullifier,
|
|
431
|
+
request.leafIndex
|
|
432
|
+
);
|
|
433
|
+
const zkProof = this.generateMockProof(request);
|
|
434
|
+
const merkleRoot = request.merkleRoot ?? new Uint8Array(32);
|
|
435
|
+
return {
|
|
436
|
+
success: true,
|
|
437
|
+
nullifierHash,
|
|
438
|
+
zkProof,
|
|
439
|
+
merkleRoot,
|
|
440
|
+
timestamp: Date.now()
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
async generateComplianceProof(commitment, associationRoot) {
|
|
444
|
+
const proofId = (0, import_ethers2.keccak256)(
|
|
445
|
+
new Uint8Array([...commitment, ...associationRoot])
|
|
446
|
+
);
|
|
447
|
+
return {
|
|
448
|
+
id: proofId,
|
|
449
|
+
associationRoot,
|
|
450
|
+
timestamp: Date.now(),
|
|
451
|
+
valid: true,
|
|
452
|
+
proof: new Uint8Array(256)
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
computeNullifierHash(nullifier, leafIndex) {
|
|
456
|
+
const indexBytes = new TextEncoder().encode(leafIndex.toString());
|
|
457
|
+
const combined = new Uint8Array([...nullifier, ...indexBytes]);
|
|
458
|
+
const hash = (0, import_ethers2.keccak256)(combined);
|
|
459
|
+
return this.hexToBytes(hash);
|
|
460
|
+
}
|
|
461
|
+
generateMockProof(request) {
|
|
462
|
+
const proof = new Uint8Array(256);
|
|
463
|
+
proof[0] = 1;
|
|
464
|
+
const amountHex = (0, import_ethers2.toBeHex)(request.amount, 32);
|
|
465
|
+
const amountBytes = this.hexToBytes(amountHex);
|
|
466
|
+
proof.set(amountBytes.slice(0, 32), 1);
|
|
467
|
+
proof.set(request.commitment.slice(0, 32), 33);
|
|
468
|
+
return proof;
|
|
469
|
+
}
|
|
470
|
+
hexToBytes(hex) {
|
|
471
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
472
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
473
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
474
|
+
bytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16);
|
|
475
|
+
}
|
|
476
|
+
return bytes;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
// src/vault.ts
|
|
481
|
+
var PrivacyVaultSDK = class {
|
|
482
|
+
provider;
|
|
483
|
+
signer = null;
|
|
484
|
+
vault;
|
|
485
|
+
_aspRegistry;
|
|
486
|
+
zkClient;
|
|
487
|
+
config;
|
|
488
|
+
_merkleTree;
|
|
489
|
+
constructor(config, signer) {
|
|
490
|
+
this.config = config;
|
|
491
|
+
this.provider = new import_ethers3.ethers.JsonRpcProvider(config.rpcUrl);
|
|
492
|
+
if (signer) {
|
|
493
|
+
this.signer = signer;
|
|
494
|
+
}
|
|
495
|
+
this.vault = new import_ethers3.ethers.Contract(
|
|
496
|
+
config.vaultAddress,
|
|
497
|
+
PRIVACY_VAULT_ABI,
|
|
498
|
+
this.signer ?? this.provider
|
|
499
|
+
);
|
|
500
|
+
this._aspRegistry = new import_ethers3.ethers.Contract(
|
|
501
|
+
config.aspRegistryAddress,
|
|
502
|
+
ASP_REGISTRY_ABI,
|
|
503
|
+
this.provider
|
|
504
|
+
);
|
|
505
|
+
this.zkClient = new ZKProofClient();
|
|
506
|
+
this._merkleTree = new MerkleTree();
|
|
507
|
+
}
|
|
508
|
+
async connect(signer) {
|
|
509
|
+
this.signer = signer;
|
|
510
|
+
this.vault = this.vault.connect(signer);
|
|
511
|
+
}
|
|
512
|
+
async deposit(amount) {
|
|
513
|
+
if (!this.signer) {
|
|
514
|
+
throw new Error("Signer required for deposits");
|
|
515
|
+
}
|
|
516
|
+
const note = generateDepositNote(amount);
|
|
517
|
+
const commitmentHex = bytesToHex(note.commitment);
|
|
518
|
+
const tx = await this.vault.deposit(commitmentHex, {
|
|
519
|
+
value: amount,
|
|
520
|
+
gasLimit: DEFAULT_GAS_LIMIT
|
|
521
|
+
});
|
|
522
|
+
const receipt = await tx.wait();
|
|
523
|
+
let leafIndex = -1;
|
|
524
|
+
const depositEvent = receipt.logs.find((log) => {
|
|
525
|
+
try {
|
|
526
|
+
const parsed = this.vault.interface.parseLog({
|
|
527
|
+
topics: log.topics,
|
|
528
|
+
data: log.data
|
|
529
|
+
});
|
|
530
|
+
return parsed?.name === "Deposit";
|
|
531
|
+
} catch {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
if (depositEvent) {
|
|
536
|
+
const parsed = this.vault.interface.parseLog({
|
|
537
|
+
topics: depositEvent.topics,
|
|
538
|
+
data: depositEvent.data
|
|
539
|
+
});
|
|
540
|
+
leafIndex = Number(parsed?.args?.leafIndex ?? -1);
|
|
541
|
+
}
|
|
542
|
+
note.leafIndex = leafIndex;
|
|
543
|
+
return {
|
|
544
|
+
success: true,
|
|
545
|
+
txHash: receipt.hash,
|
|
546
|
+
commitment: note.commitment,
|
|
547
|
+
leafIndex,
|
|
548
|
+
note
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async withdraw(note, recipient) {
|
|
552
|
+
if (!this.signer) {
|
|
553
|
+
throw new Error("Signer required for withdrawals");
|
|
554
|
+
}
|
|
555
|
+
const nullifierBigInt = bytes32ToBigInt(note.nullifierSeed);
|
|
556
|
+
const nullifierHashBigInt = computeNullifier(
|
|
557
|
+
nullifierBigInt,
|
|
558
|
+
note.leafIndex
|
|
559
|
+
);
|
|
560
|
+
const nullifierHash = bigIntToBytes32(nullifierHashBigInt);
|
|
561
|
+
const root = await this.getLatestRoot();
|
|
562
|
+
const zkProofResult = await this.zkClient.generateWithdrawalProof({
|
|
563
|
+
commitment: note.commitment,
|
|
564
|
+
nullifier: note.nullifierSeed,
|
|
565
|
+
recipient,
|
|
566
|
+
amount: note.amount,
|
|
567
|
+
leafIndex: note.leafIndex,
|
|
568
|
+
merkleRoot: root,
|
|
569
|
+
merklePath: [],
|
|
570
|
+
pathIndices: []
|
|
571
|
+
});
|
|
572
|
+
const tx = await this.vault.withdraw(
|
|
573
|
+
bytesToHex(nullifierHash),
|
|
574
|
+
bytesToHex(root),
|
|
575
|
+
recipient,
|
|
576
|
+
note.amount,
|
|
577
|
+
zkProofResult.zkProof,
|
|
578
|
+
new Uint8Array(64),
|
|
579
|
+
{ gasLimit: DEFAULT_GAS_LIMIT }
|
|
580
|
+
);
|
|
581
|
+
const receipt = await tx.wait();
|
|
582
|
+
return {
|
|
583
|
+
success: true,
|
|
584
|
+
txHash: receipt.hash,
|
|
585
|
+
zkProof: zkProofResult.zkProof,
|
|
586
|
+
nullifierHash,
|
|
587
|
+
merkleRoot: root,
|
|
588
|
+
timestamp: Date.now()
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
async getLatestRoot() {
|
|
592
|
+
const root = await this.vault.getLatestRoot();
|
|
593
|
+
return hexToBytes(root);
|
|
594
|
+
}
|
|
595
|
+
async getNextLeafIndex() {
|
|
596
|
+
const index = await this.vault.getNextLeafIndex();
|
|
597
|
+
return Number(index);
|
|
598
|
+
}
|
|
599
|
+
async isNullifierUsed(nullifier) {
|
|
600
|
+
return await this.vault.isNullifierUsed(bytesToHex(nullifier));
|
|
601
|
+
}
|
|
602
|
+
async isKnownRoot(root) {
|
|
603
|
+
return await this.vault.isKnownRoot(bytesToHex(root));
|
|
604
|
+
}
|
|
605
|
+
async getVaultStats() {
|
|
606
|
+
const [nextLeafIndex, latestRoot] = await Promise.all([
|
|
607
|
+
this.vault.getNextLeafIndex(),
|
|
608
|
+
this.vault.getLatestRoot()
|
|
609
|
+
]);
|
|
610
|
+
return {
|
|
611
|
+
totalDeposits: 0n,
|
|
612
|
+
totalWithdrawals: 0n,
|
|
613
|
+
nextLeafIndex: Number(nextLeafIndex),
|
|
614
|
+
latestRoot: hexToBytes(latestRoot)
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
getConfig() {
|
|
618
|
+
return this.config;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
622
|
+
0 && (module.exports = {
|
|
623
|
+
ASP_REGISTRY_ABI,
|
|
624
|
+
CHAIN_CONFIG,
|
|
625
|
+
CONTRACT_ADDRESSES,
|
|
626
|
+
DEFAULT_BATCH_SIZE,
|
|
627
|
+
DEFAULT_GAS_LIMIT,
|
|
628
|
+
FIELD_SIZE,
|
|
629
|
+
MERKLE_TREE_DEPTH,
|
|
630
|
+
MerkleTree,
|
|
631
|
+
POSEIDON_CONSTANTS,
|
|
632
|
+
PRIVACY_VAULT_ABI,
|
|
633
|
+
PROOF_EXPIRY_MS,
|
|
634
|
+
PrivacyVaultSDK,
|
|
635
|
+
ZERO_BYTES32,
|
|
636
|
+
ZKProofClient,
|
|
637
|
+
ZK_VERIFIER_ABI,
|
|
638
|
+
bigIntToBytes32,
|
|
639
|
+
bytes32ToBigInt,
|
|
640
|
+
bytesToHex,
|
|
641
|
+
computeCommitment,
|
|
642
|
+
computeNullifier,
|
|
643
|
+
decryptNote,
|
|
644
|
+
deserializeNote,
|
|
645
|
+
encryptNote,
|
|
646
|
+
generateDepositNote,
|
|
647
|
+
generateRandomBytes,
|
|
648
|
+
getZeroNode,
|
|
649
|
+
hexToBytes,
|
|
650
|
+
poseidonHash,
|
|
651
|
+
serializeNote
|
|
652
|
+
});
|