@veilo/sdk-core 0.3.3 → 0.4.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.
Files changed (195) hide show
  1. package/README.md +42 -3
  2. package/SHIELD_INTEGRATION.md +143 -0
  3. package/config.d.ts +2 -0
  4. package/config.js +4 -0
  5. package/dist/cjs/client.d.ts +5 -5
  6. package/dist/cjs/client.js +79 -42
  7. package/dist/cjs/compactNote.d.ts +107 -0
  8. package/dist/cjs/compactNote.js +167 -0
  9. package/dist/cjs/events.d.ts +1 -1
  10. package/dist/cjs/events.js +2 -2
  11. package/dist/cjs/idl/privacy_pool.d.ts +5 -0
  12. package/dist/cjs/idl/privacy_pool.js +15218 -0
  13. package/dist/cjs/index.d.ts +16 -13
  14. package/dist/cjs/index.js +40 -30
  15. package/dist/cjs/merkle.d.ts +13 -0
  16. package/dist/cjs/merkle.js +31 -8
  17. package/dist/cjs/program.d.ts +11 -0
  18. package/dist/cjs/program.js +26 -3
  19. package/dist/cjs/proof.d.ts +1 -1
  20. package/dist/cjs/proof.js +37 -37
  21. package/dist/cjs/prover.d.ts +1 -1
  22. package/dist/cjs/prover.js +2 -2
  23. package/dist/cjs/random.d.ts +16 -0
  24. package/dist/cjs/random.js +28 -0
  25. package/dist/cjs/relayer.d.ts +27 -4
  26. package/dist/cjs/relayer.js +16 -5
  27. package/dist/cjs/shield/alt.d.ts +87 -0
  28. package/dist/cjs/shield/alt.js +194 -0
  29. package/dist/cjs/shield/computeBudget.d.ts +61 -0
  30. package/dist/cjs/shield/computeBudget.js +64 -0
  31. package/dist/cjs/shield/errors.d.ts +58 -0
  32. package/dist/cjs/shield/errors.js +121 -0
  33. package/dist/cjs/shield/finalize.d.ts +45 -0
  34. package/dist/cjs/shield/finalize.js +119 -0
  35. package/dist/cjs/shield/index.d.ts +35 -0
  36. package/dist/cjs/shield/index.js +68 -0
  37. package/dist/cjs/shield/ix.d.ts +54 -0
  38. package/dist/cjs/shield/ix.js +119 -0
  39. package/dist/cjs/shield/owner.d.ts +36 -0
  40. package/dist/cjs/shield/owner.js +126 -0
  41. package/dist/cjs/shield/ports.d.ts +43 -0
  42. package/dist/cjs/shield/ports.js +153 -0
  43. package/dist/cjs/shield/preflight.d.ts +30 -0
  44. package/dist/cjs/shield/preflight.js +154 -0
  45. package/dist/cjs/shield/shield.d.ts +68 -0
  46. package/dist/cjs/shield/shield.js +499 -0
  47. package/dist/cjs/shield/types.d.ts +202 -0
  48. package/dist/cjs/shield/types.js +2 -0
  49. package/dist/cjs/utxo.d.ts +20 -0
  50. package/dist/cjs/utxo.js +41 -28
  51. package/dist/esm/client.d.ts +407 -0
  52. package/dist/esm/client.js +9 -5
  53. package/dist/esm/compactNote.d.ts +107 -0
  54. package/dist/esm/compactNote.js +156 -0
  55. package/dist/esm/config.d.ts +82 -0
  56. package/dist/esm/events.d.ts +77 -0
  57. package/dist/esm/events.js +1 -1
  58. package/dist/esm/idl/privacy_pool.d.ts +5 -0
  59. package/dist/esm/idl/privacy_pool.js +15216 -0
  60. package/dist/esm/index.d.ts +16 -0
  61. package/dist/esm/index.js +18 -11
  62. package/dist/esm/merkle.d.ts +77 -0
  63. package/dist/esm/merkle.js +23 -1
  64. package/dist/esm/package.json +1 -0
  65. package/dist/esm/poseidon.d.ts +29 -0
  66. package/dist/esm/program.d.ts +37 -0
  67. package/dist/esm/program.js +23 -1
  68. package/dist/esm/proof.d.ts +183 -0
  69. package/dist/esm/proof.js +2 -2
  70. package/dist/esm/prover.d.ts +54 -0
  71. package/dist/esm/prover.js +1 -1
  72. package/dist/esm/random.d.ts +16 -0
  73. package/dist/esm/random.js +21 -0
  74. package/dist/esm/relayer.d.ts +318 -0
  75. package/dist/esm/relayer.js +16 -5
  76. package/dist/esm/retry.d.ts +32 -0
  77. package/dist/esm/shield/alt.d.ts +87 -0
  78. package/dist/esm/shield/alt.js +186 -0
  79. package/dist/esm/shield/computeBudget.d.ts +61 -0
  80. package/dist/esm/shield/computeBudget.js +61 -0
  81. package/dist/esm/shield/errors.d.ts +58 -0
  82. package/dist/esm/shield/errors.js +115 -0
  83. package/dist/esm/shield/finalize.d.ts +45 -0
  84. package/dist/esm/shield/finalize.js +83 -0
  85. package/dist/esm/shield/index.d.ts +35 -0
  86. package/dist/esm/shield/index.js +32 -0
  87. package/dist/esm/shield/ix.d.ts +54 -0
  88. package/dist/esm/shield/ix.js +82 -0
  89. package/dist/esm/shield/owner.d.ts +36 -0
  90. package/dist/esm/shield/owner.js +122 -0
  91. package/dist/esm/shield/ports.d.ts +43 -0
  92. package/dist/esm/shield/ports.js +147 -0
  93. package/dist/esm/shield/preflight.d.ts +30 -0
  94. package/dist/esm/shield/preflight.js +151 -0
  95. package/dist/esm/shield/shield.d.ts +68 -0
  96. package/dist/esm/shield/shield.js +492 -0
  97. package/dist/esm/shield/types.d.ts +202 -0
  98. package/dist/esm/shield/types.js +1 -0
  99. package/dist/esm/utxo.d.ts +235 -0
  100. package/dist/esm/utxo.js +18 -8
  101. package/dist/src/client.d.ts +407 -0
  102. package/dist/src/client.js +951 -0
  103. package/dist/src/compactNote.d.ts +107 -0
  104. package/dist/src/compactNote.js +167 -0
  105. package/dist/src/config.d.ts +82 -0
  106. package/dist/src/config.js +57 -0
  107. package/dist/src/events.d.ts +77 -0
  108. package/dist/src/events.js +167 -0
  109. package/dist/src/idl/privacy_pool.d.ts +5 -0
  110. package/dist/src/idl/privacy_pool.js +15218 -0
  111. package/dist/src/index.d.ts +16 -0
  112. package/dist/src/index.js +67 -0
  113. package/dist/src/merkle.d.ts +77 -0
  114. package/dist/src/merkle.js +156 -0
  115. package/dist/src/poseidon.d.ts +29 -0
  116. package/dist/src/poseidon.js +100 -0
  117. package/dist/src/program.d.ts +37 -0
  118. package/dist/src/program.js +61 -0
  119. package/dist/src/proof.d.ts +183 -0
  120. package/dist/src/proof.js +292 -0
  121. package/dist/src/prover.d.ts +54 -0
  122. package/dist/src/prover.js +112 -0
  123. package/dist/src/random.d.ts +16 -0
  124. package/dist/src/random.js +28 -0
  125. package/dist/src/relayer.d.ts +318 -0
  126. package/dist/src/relayer.js +257 -0
  127. package/dist/src/retry.d.ts +32 -0
  128. package/dist/src/retry.js +75 -0
  129. package/dist/src/shield/alt.d.ts +87 -0
  130. package/dist/src/shield/alt.js +194 -0
  131. package/dist/src/shield/computeBudget.d.ts +61 -0
  132. package/dist/src/shield/computeBudget.js +64 -0
  133. package/dist/src/shield/errors.d.ts +58 -0
  134. package/dist/src/shield/errors.js +121 -0
  135. package/dist/src/shield/finalize.d.ts +45 -0
  136. package/dist/src/shield/finalize.js +119 -0
  137. package/dist/src/shield/index.d.ts +35 -0
  138. package/dist/src/shield/index.js +68 -0
  139. package/dist/src/shield/ix.d.ts +54 -0
  140. package/dist/src/shield/ix.js +119 -0
  141. package/dist/src/shield/owner.d.ts +36 -0
  142. package/dist/src/shield/owner.js +126 -0
  143. package/dist/src/shield/ports.d.ts +43 -0
  144. package/dist/src/shield/ports.js +153 -0
  145. package/dist/src/shield/preflight.d.ts +30 -0
  146. package/dist/src/shield/preflight.js +154 -0
  147. package/dist/src/shield/shield.d.ts +68 -0
  148. package/dist/src/shield/shield.js +499 -0
  149. package/dist/src/shield/types.d.ts +202 -0
  150. package/dist/src/shield/types.js +2 -0
  151. package/dist/src/utxo.d.ts +235 -0
  152. package/dist/src/utxo.js +407 -0
  153. package/dist/tests/compact-note.test.d.ts +1 -0
  154. package/dist/tests/compact-note.test.js +173 -0
  155. package/dist/tests/config.test.d.ts +1 -0
  156. package/dist/tests/config.test.js +102 -0
  157. package/dist/tests/edge-cases.test.d.ts +1 -0
  158. package/dist/tests/edge-cases.test.js +220 -0
  159. package/dist/tests/encryption.test.d.ts +1 -0
  160. package/dist/tests/encryption.test.js +215 -0
  161. package/dist/tests/events.test.d.ts +1 -0
  162. package/dist/tests/events.test.js +78 -0
  163. package/dist/tests/multi-tree.test.d.ts +1 -0
  164. package/dist/tests/multi-tree.test.js +405 -0
  165. package/dist/tests/pda.test.d.ts +1 -0
  166. package/dist/tests/pda.test.js +229 -0
  167. package/dist/tests/poseidon-builder-parity.test.d.ts +1 -0
  168. package/dist/tests/poseidon-builder-parity.test.js +72 -0
  169. package/dist/tests/poseidon.test.d.ts +1 -0
  170. package/dist/tests/poseidon.test.js +142 -0
  171. package/dist/tests/proof.test.d.ts +1 -0
  172. package/dist/tests/proof.test.js +296 -0
  173. package/dist/tests/relayer.test.d.ts +1 -0
  174. package/dist/tests/relayer.test.js +271 -0
  175. package/dist/tests/sdk.integration.test.d.ts +1 -0
  176. package/dist/tests/sdk.integration.test.js +330 -0
  177. package/dist/tests/shield-owner.test.d.ts +1 -0
  178. package/dist/tests/shield-owner.test.js +89 -0
  179. package/dist/tests/shield-preflight.test.d.ts +1 -0
  180. package/dist/tests/shield-preflight.test.js +87 -0
  181. package/dist/tests/shield-realproof.test.d.ts +1 -0
  182. package/dist/tests/shield-realproof.test.js +272 -0
  183. package/dist/tests/shield.test.d.ts +1 -0
  184. package/dist/tests/shield.test.js +403 -0
  185. package/dist/tests/utxo.test.d.ts +1 -0
  186. package/dist/tests/utxo.test.js +140 -0
  187. package/package.json +69 -11
  188. package/poseidon.d.ts +2 -0
  189. package/poseidon.js +4 -0
  190. package/proof.d.ts +2 -0
  191. package/proof.js +4 -0
  192. package/prover.d.ts +2 -0
  193. package/prover.js +4 -0
  194. package/shield.d.ts +2 -0
  195. package/shield.js +4 -0
@@ -0,0 +1,405 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ // tests/multi-tree.test.ts
40
+ require("mocha");
41
+ const assert_1 = require("assert");
42
+ const anchor = __importStar(require("@coral-xyz/anchor"));
43
+ const web3_js_1 = require("@solana/web3.js");
44
+ const fs_1 = __importDefault(require("fs"));
45
+ const os_1 = __importDefault(require("os"));
46
+ const path_1 = __importDefault(require("path"));
47
+ const src_1 = require("../src");
48
+ // ---- IDL + Provider helpers ----
49
+ function loadIdl() {
50
+ const idlPath = path_1.default.join(__dirname, "../../../privacy-program/target/idl/privacy_pool.json");
51
+ if (!fs_1.default.existsSync(idlPath)) {
52
+ return null;
53
+ }
54
+ const raw = fs_1.default.readFileSync(idlPath, "utf8");
55
+ return JSON.parse(raw);
56
+ }
57
+ function makeProvider() {
58
+ try {
59
+ const url = process.env.ANCHOR_PROVIDER_URL ?? "http://127.0.0.1:8899";
60
+ const connection = new anchor.web3.Connection(url, "confirmed");
61
+ const keypairPath = process.env.ANCHOR_WALLET ??
62
+ path_1.default.join(os_1.default.homedir(), ".config", "solana", "id.json");
63
+ if (!fs_1.default.existsSync(keypairPath)) {
64
+ return null;
65
+ }
66
+ const secret = JSON.parse(fs_1.default.readFileSync(keypairPath, "utf8"));
67
+ const kp = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(secret));
68
+ const wallet = new anchor.Wallet(kp);
69
+ return new anchor.AnchorProvider(connection, wallet, {
70
+ commitment: "confirmed",
71
+ preflightCommitment: "confirmed",
72
+ });
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ describe("Constants", function () {
79
+ it("MERKLE_TREE_DEPTH is 22", () => {
80
+ assert_1.strict.equal(src_1.MERKLE_TREE_DEPTH, 22);
81
+ });
82
+ it("ROOT_HISTORY_SIZE is 256", () => {
83
+ assert_1.strict.equal(src_1.ROOT_HISTORY_SIZE, 256);
84
+ });
85
+ });
86
+ describe("MerkleTree with new depth", function () {
87
+ this.timeout(30000);
88
+ before(async () => {
89
+ await (0, src_1.initPoseidon)();
90
+ });
91
+ it("creates tree with default depth of 22", () => {
92
+ const tree = new src_1.MerkleTree();
93
+ assert_1.strict.equal(tree.depth, 22);
94
+ assert_1.strict.equal(tree.capacity, 2 ** 22); // 4,194,304 leaves
95
+ });
96
+ it("can still create tree with explicit depth", () => {
97
+ const tree = new src_1.MerkleTree(16);
98
+ assert_1.strict.equal(tree.depth, 16);
99
+ assert_1.strict.equal(tree.capacity, 2 ** 16);
100
+ });
101
+ it("generates correct path length for depth 22", () => {
102
+ const tree = new src_1.MerkleTree(22);
103
+ const keypair = (0, src_1.generateKeypair)();
104
+ const utxo = (0, src_1.createUTXO)({
105
+ amount: (0, src_1.sol)(1),
106
+ pubkey: keypair.publicKey,
107
+ mintAddress: src_1.NATIVE_SOL_MINT,
108
+ });
109
+ const { index } = tree.insert(utxo.commitment);
110
+ const path = tree.getPath(index);
111
+ assert_1.strict.equal(path.path.length, 22);
112
+ assert_1.strict.equal(path.indices.length, 22);
113
+ });
114
+ });
115
+ describe("Multi-tree PDA scenarios", function () {
116
+ const programId = new web3_js_1.PublicKey("6Cq2rfH7hcreu6Lz4LFoVvZC37Q5uzNq1cStuTnjFdBU");
117
+ it("each tree ID produces unique PDA", () => {
118
+ const pdas = new Set();
119
+ for (let treeId = 0; treeId < 10; treeId++) {
120
+ const pda = (0, src_1.getNoteTreePda)(programId, src_1.NATIVE_SOL_MINT, treeId);
121
+ const pdaStr = pda.toBase58();
122
+ assert_1.strict.ok(!pdas.has(pdaStr), `Tree ${treeId} should have unique PDA`);
123
+ pdas.add(pdaStr);
124
+ }
125
+ assert_1.strict.equal(pdas.size, 10);
126
+ });
127
+ it("nullifier markers are global — same nullifier always produces same PDA", () => {
128
+ const nullifier = new Uint8Array(32);
129
+ nullifier[0] = 0x42;
130
+ // No tree_id in seeds: one spent nullifier blocks re-use across all trees.
131
+ const marker1 = (0, src_1.getNullifierMarkerPda)(programId, src_1.NATIVE_SOL_MINT, nullifier);
132
+ const marker2 = (0, src_1.getNullifierMarkerPda)(programId, src_1.NATIVE_SOL_MINT, nullifier);
133
+ assert_1.strict.equal(marker1.toBase58(), marker2.toBase58(), "Same nullifier should always produce the same marker");
134
+ });
135
+ it("different nullifiers produce different markers", () => {
136
+ const nullifiers = [
137
+ new Uint8Array(32).fill(0xab),
138
+ new Uint8Array(32).fill(0xcd),
139
+ new Uint8Array(32).fill(0xef),
140
+ ];
141
+ const markers = nullifiers.map((n) => (0, src_1.getNullifierMarkerPda)(programId, src_1.NATIVE_SOL_MINT, n).toBase58());
142
+ assert_1.strict.equal(new Set(markers).size, 3);
143
+ });
144
+ });
145
+ describe("High-level helper function signatures", function () {
146
+ this.timeout(10000);
147
+ before(async () => {
148
+ await (0, src_1.initPoseidon)();
149
+ });
150
+ it("deposit accepts treeId parameter", () => {
151
+ // Type check - deposit should accept treeId
152
+ const depositParams = {
153
+ program: {},
154
+ depositor: {},
155
+ amount: 1000n,
156
+ mintAddress: src_1.NATIVE_SOL_MINT,
157
+ recipientPubkey: 12345n,
158
+ tree: new src_1.MerkleTree(16),
159
+ proofBuilder: async () => ({}),
160
+ treeId: 5, // This should be accepted
161
+ };
162
+ // If this compiles, the type is correct
163
+ assert_1.strict.equal(depositParams.treeId, 5);
164
+ });
165
+ it("withdraw requires inputTreeId and outputTreeId", () => {
166
+ const withdrawParams = {
167
+ program: {},
168
+ relayer: web3_js_1.Keypair.generate(),
169
+ recipient: web3_js_1.Keypair.generate().publicKey,
170
+ mintAddress: src_1.NATIVE_SOL_MINT,
171
+ inputUTXOs: [{}, {}],
172
+ changePubkey: 12345n,
173
+ withdrawAmount: 1000n,
174
+ fee: 10n,
175
+ refund: 0n,
176
+ root: new Uint8Array(32),
177
+ inputTreeId: 0,
178
+ outputTreeId: 1,
179
+ tree: new src_1.MerkleTree(16),
180
+ proofBuilder: async () => ({}),
181
+ };
182
+ assert_1.strict.equal(withdrawParams.inputTreeId, 0);
183
+ assert_1.strict.equal(withdrawParams.outputTreeId, 1);
184
+ });
185
+ it("privateTransfer requires inputTreeId and outputTreeId", () => {
186
+ const transferParams = {
187
+ program: {},
188
+ relayer: web3_js_1.Keypair.generate(),
189
+ mintAddress: src_1.NATIVE_SOL_MINT,
190
+ inputUTXOs: [{}, {}],
191
+ outputPubkeys: [123n, 456n],
192
+ outputAmounts: [500n, 500n],
193
+ root: new Uint8Array(32),
194
+ inputTreeId: 2,
195
+ outputTreeId: 3,
196
+ tree: new src_1.MerkleTree(16),
197
+ proofBuilder: async () => ({}),
198
+ };
199
+ assert_1.strict.equal(transferParams.inputTreeId, 2);
200
+ assert_1.strict.equal(transferParams.outputTreeId, 3);
201
+ });
202
+ it("transact requires inputTreeId and outputTreeId", () => {
203
+ const transactParams = {
204
+ program: {},
205
+ relayer: web3_js_1.Keypair.generate(),
206
+ recipient: web3_js_1.Keypair.generate().publicKey,
207
+ mintAddress: src_1.NATIVE_SOL_MINT,
208
+ root: new Uint8Array(32),
209
+ inputTreeId: 0,
210
+ outputTreeId: 0,
211
+ publicAmount: 1000n,
212
+ inputNullifiers: [new Uint8Array(32), new Uint8Array(32)],
213
+ outputCommitments: [new Uint8Array(32), new Uint8Array(32)],
214
+ extData: {
215
+ recipient: web3_js_1.Keypair.generate().publicKey,
216
+ relayer: web3_js_1.Keypair.generate().publicKey,
217
+ fee: 0n,
218
+ refund: 0n,
219
+ },
220
+ proof: {
221
+ proofA: new Array(64).fill(0),
222
+ proofB: new Array(128).fill(0),
223
+ proofC: new Array(64).fill(0),
224
+ },
225
+ };
226
+ assert_1.strict.equal(transactParams.inputTreeId, 0);
227
+ assert_1.strict.equal(transactParams.outputTreeId, 0);
228
+ });
229
+ });
230
+ describe("PrivacyConfigAccount type", function () {
231
+ it("has correct shape with new fields", () => {
232
+ const mockConfig = {
233
+ bump: 255,
234
+ vaultBump: 254,
235
+ admin: web3_js_1.Keypair.generate().publicKey,
236
+ feeBps: 50,
237
+ feeErrorMarginBps: 10,
238
+ minWithdrawalFee: 1000n,
239
+ minSwapFee: 0n,
240
+ swapFeeBps: 0,
241
+ totalTvl: 1000000n,
242
+ mintAddress: src_1.NATIVE_SOL_MINT,
243
+ minDepositAmount: 100000n,
244
+ maxDepositAmount: 10000000000n,
245
+ minWithdrawAmount: 100000n,
246
+ maxWithdrawAmount: 10000000000n,
247
+ numRelayers: 2,
248
+ relayers: [web3_js_1.Keypair.generate().publicKey, web3_js_1.Keypair.generate().publicKey],
249
+ numTrees: 3,
250
+ nextTreeIndex: 150,
251
+ };
252
+ // Verify new fields exist
253
+ assert_1.strict.equal(mockConfig.feeErrorMarginBps, 10);
254
+ assert_1.strict.equal(mockConfig.numTrees, 3);
255
+ assert_1.strict.equal(mockConfig.nextTreeIndex, 150);
256
+ // Verify paused field is NOT present (removed)
257
+ assert_1.strict.ok(!("paused" in mockConfig));
258
+ });
259
+ });
260
+ // Integration tests that require a running validator
261
+ describe("Multi-tree integration tests", function () {
262
+ this.timeout(120000);
263
+ const provider = makeProvider();
264
+ const idl = loadIdl();
265
+ // Skip if no provider or IDL available
266
+ before(function () {
267
+ if (!provider || !idl) {
268
+ console.log("Skipping integration tests: no provider or IDL available");
269
+ this.skip();
270
+ }
271
+ });
272
+ let program;
273
+ let wallet;
274
+ let connection;
275
+ // Use a unique mint for these tests to avoid conflicts
276
+ let testMint;
277
+ async function airdropAndConfirm(pubkey, lamports) {
278
+ const sig = await connection.requestAirdrop(pubkey, lamports);
279
+ const latestBlockhash = await connection.getLatestBlockhash();
280
+ await connection.confirmTransaction({
281
+ signature: sig,
282
+ blockhash: latestBlockhash.blockhash,
283
+ lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
284
+ });
285
+ }
286
+ before(async function () {
287
+ if (!provider || !idl) {
288
+ return;
289
+ }
290
+ await (0, src_1.initPoseidon)();
291
+ anchor.setProvider(provider);
292
+ program = new anchor.Program(idl, provider);
293
+ wallet = provider.wallet;
294
+ connection = provider.connection;
295
+ // Use a unique "mint" for these tests (just a random pubkey for native SOL tests)
296
+ // In production, you'd use NATIVE_SOL_MINT or an actual SPL token mint
297
+ testMint = src_1.NATIVE_SOL_MINT;
298
+ });
299
+ it("initializes global config if needed", async function () {
300
+ if (!provider)
301
+ this.skip();
302
+ const globalConfig = (0, src_1.getGlobalConfigPda)(program.programId);
303
+ const existingConfig = await connection.getAccountInfo(globalConfig);
304
+ if (!existingConfig) {
305
+ await airdropAndConfirm(wallet.publicKey, 2 * web3_js_1.LAMPORTS_PER_SOL);
306
+ await (0, src_1.initializeGlobalConfig)({
307
+ program,
308
+ admin: wallet,
309
+ payer: wallet.payer,
310
+ });
311
+ }
312
+ });
313
+ it("initializes pool with tree 0", async function () {
314
+ if (!provider)
315
+ this.skip();
316
+ const { config } = (0, src_1.getPoolPdas)(program.programId, testMint);
317
+ const existingConfig = await connection.getAccountInfo(config);
318
+ if (!existingConfig) {
319
+ await airdropAndConfirm(wallet.publicKey, 5 * web3_js_1.LAMPORTS_PER_SOL);
320
+ await (0, src_1.initializePool)({
321
+ program,
322
+ admin: wallet,
323
+ payer: wallet.payer,
324
+ feeBps: 50,
325
+ mintAddress: testMint,
326
+ minDepositAmount: (0, src_1.sol)(0.01),
327
+ maxDepositAmount: (0, src_1.sol)(100),
328
+ minWithdrawAmount: (0, src_1.sol)(0.01),
329
+ maxWithdrawAmount: (0, src_1.sol)(100),
330
+ });
331
+ }
332
+ // Verify tree 0 exists
333
+ const tree0Pda = (0, src_1.getNoteTreePda)(program.programId, testMint, 0);
334
+ const tree0Account = await connection.getAccountInfo(tree0Pda);
335
+ assert_1.strict.ok(tree0Account !== null, "Tree 0 should exist after initialization");
336
+ });
337
+ it("fetches pool config with new fields", async function () {
338
+ if (!provider)
339
+ this.skip();
340
+ const config = await (0, src_1.getPoolConfig)(program, testMint);
341
+ assert_1.strict.ok(typeof config.bump === "number");
342
+ assert_1.strict.ok(typeof config.vaultBump === "number");
343
+ assert_1.strict.ok(config.admin instanceof web3_js_1.PublicKey);
344
+ assert_1.strict.ok(typeof config.feeBps === "number");
345
+ assert_1.strict.ok(typeof config.feeErrorMarginBps === "number");
346
+ assert_1.strict.ok(typeof config.minWithdrawalFee === "bigint");
347
+ assert_1.strict.ok(typeof config.totalTvl === "bigint");
348
+ assert_1.strict.ok(config.mintAddress instanceof web3_js_1.PublicKey);
349
+ assert_1.strict.ok(typeof config.numTrees === "number");
350
+ assert_1.strict.ok(typeof config.nextTreeIndex === "number");
351
+ console.log("Pool config:", {
352
+ feeBps: config.feeBps,
353
+ feeErrorMarginBps: config.feeErrorMarginBps,
354
+ numTrees: config.numTrees,
355
+ nextTreeIndex: config.nextTreeIndex,
356
+ });
357
+ });
358
+ it("updates pool config with feeErrorMarginBps", async function () {
359
+ if (!provider)
360
+ this.skip();
361
+ const newFeeErrorMarginBps = 25;
362
+ await (0, src_1.updatePoolConfig)({
363
+ program,
364
+ admin: wallet,
365
+ mintAddress: testMint,
366
+ feeErrorMarginBps: newFeeErrorMarginBps,
367
+ });
368
+ const config = await (0, src_1.getPoolConfig)(program, testMint);
369
+ assert_1.strict.equal(config.feeErrorMarginBps, newFeeErrorMarginBps, "feeErrorMarginBps should be updated");
370
+ });
371
+ it("adds a second merkle tree", async function () {
372
+ if (!provider)
373
+ this.skip();
374
+ const configBefore = await (0, src_1.getPoolConfig)(program, testMint);
375
+ const expectedTreeId = configBefore.numTrees;
376
+ // Check if tree already exists
377
+ const tree1Pda = (0, src_1.getNoteTreePda)(program.programId, testMint, expectedTreeId);
378
+ const existingTree = await connection.getAccountInfo(tree1Pda);
379
+ if (!existingTree) {
380
+ // Use wallet.payer (admin) since only admin/relayers can add trees
381
+ await (0, src_1.addMerkleTree)({
382
+ program,
383
+ relayer: wallet.payer,
384
+ mintAddress: testMint,
385
+ treeId: expectedTreeId,
386
+ });
387
+ }
388
+ // Verify tree exists
389
+ const treeAccount = await connection.getAccountInfo(tree1Pda);
390
+ assert_1.strict.ok(treeAccount !== null, `Tree ${expectedTreeId} should exist`);
391
+ // Verify numTrees increased
392
+ const configAfter = await (0, src_1.getPoolConfig)(program, testMint);
393
+ assert_1.strict.ok(configAfter.numTrees > configBefore.numTrees || existingTree !== null, "numTrees should have increased");
394
+ });
395
+ it("can derive PDAs for multiple trees", async function () {
396
+ if (!provider)
397
+ this.skip();
398
+ const config = await (0, src_1.getPoolConfig)(program, testMint);
399
+ for (let treeId = 0; treeId < config.numTrees; treeId++) {
400
+ const treePda = (0, src_1.getNoteTreePda)(program.programId, testMint, treeId);
401
+ const treeAccount = await connection.getAccountInfo(treePda);
402
+ assert_1.strict.ok(treeAccount !== null, `Tree ${treeId} should exist (numTrees=${config.numTrees})`);
403
+ }
404
+ });
405
+ });
@@ -0,0 +1 @@
1
+ import "mocha";
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("mocha");
4
+ const assert_1 = require("assert");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ const src_1 = require("../src");
7
+ // Test program ID (use a deterministic one for reproducible tests)
8
+ const TEST_PROGRAM_ID = new web3_js_1.PublicKey("6Cq2rfH7hcreu6Lz4LFoVvZC37Q5uzNq1cStuTnjFdBU");
9
+ describe("PDA Derivation", function () {
10
+ describe("getPoolPdas", () => {
11
+ it("returns config, vault, and nullifiers PDAs", () => {
12
+ const pdas = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT);
13
+ assert_1.strict.ok(pdas.config instanceof web3_js_1.PublicKey);
14
+ assert_1.strict.ok(pdas.vault instanceof web3_js_1.PublicKey);
15
+ assert_1.strict.ok(pdas.nullifiers instanceof web3_js_1.PublicKey);
16
+ // Should NOT have noteTree (removed in favor of getNoteTreePda)
17
+ assert_1.strict.ok(!("noteTree" in pdas), "noteTree should not be in getPoolPdas result");
18
+ });
19
+ it("derives different PDAs for different mints", () => {
20
+ const mint1 = src_1.NATIVE_SOL_MINT;
21
+ const mint2 = web3_js_1.Keypair.generate().publicKey;
22
+ const pdas1 = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, mint1);
23
+ const pdas2 = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, mint2);
24
+ assert_1.strict.notEqual(pdas1.config.toBase58(), pdas2.config.toBase58(), "Different mints should have different config PDAs");
25
+ assert_1.strict.notEqual(pdas1.vault.toBase58(), pdas2.vault.toBase58(), "Different mints should have different vault PDAs");
26
+ });
27
+ it("derives PDAs deterministically", () => {
28
+ const pdas1 = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT);
29
+ const pdas2 = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT);
30
+ assert_1.strict.equal(pdas1.config.toBase58(), pdas2.config.toBase58());
31
+ assert_1.strict.equal(pdas1.vault.toBase58(), pdas2.vault.toBase58());
32
+ assert_1.strict.equal(pdas1.nullifiers.toBase58(), pdas2.nullifiers.toBase58());
33
+ });
34
+ it("uses v3 seed for config PDA", () => {
35
+ // Manually derive with v3 seed to verify
36
+ const [expectedConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("privacy_config_v3"), src_1.NATIVE_SOL_MINT.toBuffer()], TEST_PROGRAM_ID);
37
+ const pdas = (0, src_1.getPoolPdas)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT);
38
+ assert_1.strict.equal(pdas.config.toBase58(), expectedConfig.toBase58(), "Config PDA should use privacy_config_v3 seed");
39
+ });
40
+ });
41
+ describe("getGlobalConfigPda", () => {
42
+ it("returns a valid PublicKey", () => {
43
+ const globalConfig = (0, src_1.getGlobalConfigPda)(TEST_PROGRAM_ID);
44
+ assert_1.strict.ok(globalConfig instanceof web3_js_1.PublicKey);
45
+ });
46
+ it("derives deterministically", () => {
47
+ const pda1 = (0, src_1.getGlobalConfigPda)(TEST_PROGRAM_ID);
48
+ const pda2 = (0, src_1.getGlobalConfigPda)(TEST_PROGRAM_ID);
49
+ assert_1.strict.equal(pda1.toBase58(), pda2.toBase58());
50
+ });
51
+ it("uses global_config_v1 seed", () => {
52
+ const [expected] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_config_v1")], TEST_PROGRAM_ID);
53
+ const pda = (0, src_1.getGlobalConfigPda)(TEST_PROGRAM_ID);
54
+ assert_1.strict.equal(pda.toBase58(), expected.toBase58());
55
+ });
56
+ });
57
+ describe("getNoteTreePda", () => {
58
+ it("returns a valid PublicKey", () => {
59
+ const noteTree = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 0);
60
+ assert_1.strict.ok(noteTree instanceof web3_js_1.PublicKey);
61
+ });
62
+ it("derives different PDAs for different tree IDs", () => {
63
+ const tree0 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 0);
64
+ const tree1 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 1);
65
+ const tree2 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 2);
66
+ assert_1.strict.notEqual(tree0.toBase58(), tree1.toBase58());
67
+ assert_1.strict.notEqual(tree1.toBase58(), tree2.toBase58());
68
+ assert_1.strict.notEqual(tree0.toBase58(), tree2.toBase58());
69
+ });
70
+ it("derives different PDAs for different mints", () => {
71
+ const mint1 = src_1.NATIVE_SOL_MINT;
72
+ const mint2 = web3_js_1.Keypair.generate().publicKey;
73
+ const tree1 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, mint1, 0);
74
+ const tree2 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, mint2, 0);
75
+ assert_1.strict.notEqual(tree1.toBase58(), tree2.toBase58());
76
+ });
77
+ it("derives deterministically", () => {
78
+ const tree1 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 5);
79
+ const tree2 = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, 5);
80
+ assert_1.strict.equal(tree1.toBase58(), tree2.toBase58());
81
+ });
82
+ it("uses correct seed format with tree ID", () => {
83
+ const treeId = 3;
84
+ const treeIdBuffer = Buffer.alloc(2);
85
+ treeIdBuffer.writeUInt16LE(treeId);
86
+ const [expected] = web3_js_1.PublicKey.findProgramAddressSync([
87
+ Buffer.from("privacy_note_tree_v3"),
88
+ src_1.NATIVE_SOL_MINT.toBuffer(),
89
+ treeIdBuffer,
90
+ ], TEST_PROGRAM_ID);
91
+ const actual = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, treeId);
92
+ assert_1.strict.equal(actual.toBase58(), expected.toBase58());
93
+ });
94
+ it("handles large tree IDs", () => {
95
+ const largeTreeId = 255;
96
+ const tree = (0, src_1.getNoteTreePda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, largeTreeId);
97
+ assert_1.strict.ok(tree instanceof web3_js_1.PublicKey);
98
+ });
99
+ });
100
+ describe("getNullifierMarkerPda", () => {
101
+ const mockNullifier = new Uint8Array(32).fill(1);
102
+ it("returns a valid PublicKey", () => {
103
+ const marker = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
104
+ assert_1.strict.ok(marker instanceof web3_js_1.PublicKey);
105
+ });
106
+ it("is global — same nullifier always produces the same marker", () => {
107
+ // Nullifier markers have no tree_id in their seeds; one spent nullifier
108
+ // prevents re-use across all trees in the pool.
109
+ const marker1 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
110
+ const marker2 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
111
+ assert_1.strict.equal(marker1.toBase58(), marker2.toBase58(), "Same nullifier should always produce the same marker");
112
+ });
113
+ it("derives different PDAs for different nullifiers", () => {
114
+ const nullifier1 = new Uint8Array(32).fill(1);
115
+ const nullifier2 = new Uint8Array(32).fill(2);
116
+ const marker1 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, nullifier1);
117
+ const marker2 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, nullifier2);
118
+ assert_1.strict.notEqual(marker1.toBase58(), marker2.toBase58());
119
+ });
120
+ it("derives different PDAs for different mints", () => {
121
+ const otherMint = web3_js_1.Keypair.generate().publicKey;
122
+ const marker1 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
123
+ const marker2 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, otherMint, mockNullifier);
124
+ assert_1.strict.notEqual(marker1.toBase58(), marker2.toBase58());
125
+ });
126
+ it("derives deterministically", () => {
127
+ const marker1 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
128
+ const marker2 = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
129
+ assert_1.strict.equal(marker1.toBase58(), marker2.toBase58());
130
+ });
131
+ it("uses nullifier_v3 seed without tree ID", () => {
132
+ const [expected] = web3_js_1.PublicKey.findProgramAddressSync([
133
+ Buffer.from("nullifier_v3"),
134
+ src_1.NATIVE_SOL_MINT.toBuffer(),
135
+ mockNullifier,
136
+ ], TEST_PROGRAM_ID);
137
+ const actual = (0, src_1.getNullifierMarkerPda)(TEST_PROGRAM_ID, src_1.NATIVE_SOL_MINT, mockNullifier);
138
+ assert_1.strict.equal(actual.toBase58(), expected.toBase58());
139
+ });
140
+ });
141
+ // -------------------------------------------------------------------------
142
+ // getSwapExecutorPda
143
+ // -------------------------------------------------------------------------
144
+ describe("getSwapExecutorPda", () => {
145
+ const sourceMint = new web3_js_1.PublicKey("So11111111111111111111111111111111111111112");
146
+ const destMint = new web3_js_1.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
147
+ const relayer = web3_js_1.Keypair.generate().publicKey;
148
+ const inputNullifier0 = new Uint8Array(32).fill(0x01);
149
+ it("returns a valid PublicKey", () => {
150
+ const executor = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
151
+ assert_1.strict.ok(executor instanceof web3_js_1.PublicKey);
152
+ });
153
+ it("is deterministic", () => {
154
+ const e1 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
155
+ const e2 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
156
+ assert_1.strict.equal(e1.toBase58(), e2.toBase58());
157
+ });
158
+ it("uses swap_executor seed", () => {
159
+ const [expected] = web3_js_1.PublicKey.findProgramAddressSync([
160
+ Buffer.from("swap_executor"),
161
+ sourceMint.toBuffer(),
162
+ destMint.toBuffer(),
163
+ inputNullifier0,
164
+ relayer.toBuffer(),
165
+ ], TEST_PROGRAM_ID);
166
+ const actual = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
167
+ assert_1.strict.equal(actual.toBase58(), expected.toBase58());
168
+ });
169
+ it("produces different PDA for different nullifiers", () => {
170
+ const nullifier2 = new Uint8Array(32).fill(0x02);
171
+ const e1 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
172
+ const e2 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, nullifier2, relayer);
173
+ assert_1.strict.notEqual(e1.toBase58(), e2.toBase58());
174
+ });
175
+ it("produces different PDA for different relayers", () => {
176
+ const relayer2 = web3_js_1.Keypair.generate().publicKey;
177
+ const e1 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer);
178
+ const e2 = (0, src_1.getSwapExecutorPda)(TEST_PROGRAM_ID, sourceMint, destMint, inputNullifier0, relayer2);
179
+ assert_1.strict.notEqual(e1.toBase58(), e2.toBase58());
180
+ });
181
+ });
182
+ });
183
+ // =============================================================================
184
+ // parseOnChainError
185
+ // =============================================================================
186
+ describe("parseOnChainError", function () {
187
+ it("maps hex program error code 0x1770 (6000) to the pause message", () => {
188
+ const err = new Error("custom program error: 0x1770");
189
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "The pool is temporarily paused. Please try again later.");
190
+ });
191
+ it("maps hex program error code 0x1775 (6005) to double-spend message", () => {
192
+ const err = new Error("custom program error: 0x1775");
193
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "These funds have already been spent.");
194
+ });
195
+ it("maps decimal error number 6012 to proof verification message", () => {
196
+ const err = new Error("Error number: 6012");
197
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "Zero-knowledge proof verification failed. Please try again.");
198
+ });
199
+ it("maps 6035 to below-minimum message", () => {
200
+ const err = new Error("custom program error: 0x178b"); // 0x178b = 6027? let me recalculate: 6035 = 0x1793
201
+ // 6035 decimal = 0x1793 hex
202
+ const err2 = new Error("Error number: 6035");
203
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err2), "Amount is below the pool minimum.");
204
+ });
205
+ it("returns blockhash-expired message for 'blockhash not found'", () => {
206
+ const err = new Error("Blockhash not found");
207
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "The transaction expired before it could be confirmed. Please try again.");
208
+ });
209
+ it("returns timeout message for 'timed out' errors", () => {
210
+ const err = new Error("Request timed out");
211
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "The request timed out. Please try again.");
212
+ });
213
+ it("returns simulation failed message", () => {
214
+ const err = new Error("Transaction simulation failed");
215
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "Transaction simulation failed. Please refresh and try again.");
216
+ });
217
+ it("returns generic fallback for unknown error", () => {
218
+ const err = new Error("some unknown error");
219
+ assert_1.strict.equal((0, src_1.parseOnChainError)(err), "The transaction could not be completed. Your funds are safe — please try again.");
220
+ });
221
+ it("handles non-Error objects with toString()", () => {
222
+ const result = (0, src_1.parseOnChainError)("some unknown string error");
223
+ assert_1.strict.equal(result, "The transaction could not be completed. Your funds are safe — please try again.");
224
+ });
225
+ it("handles null/undefined gracefully", () => {
226
+ const result = (0, src_1.parseOnChainError)(null);
227
+ assert_1.strict.equal(result, "The transaction could not be completed. Your funds are safe — please try again.");
228
+ });
229
+ });
@@ -0,0 +1 @@
1
+ import "mocha";