@veilo/sdk-core 0.5.0 → 0.6.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 (236) hide show
  1. package/README.md +237 -19
  2. package/accounts.d.ts +2 -0
  3. package/accounts.js +3 -0
  4. package/cloak.d.ts +2 -0
  5. package/cloak.js +3 -0
  6. package/config.d.ts +2 -0
  7. package/config.js +3 -0
  8. package/dist/cjs/events/index.d.ts +6 -0
  9. package/dist/cjs/events/index.js +22 -0
  10. package/dist/cjs/events/parsing.d.ts +25 -0
  11. package/dist/cjs/events/parsing.js +158 -0
  12. package/dist/cjs/events/position.d.ts +26 -0
  13. package/dist/cjs/events/position.js +53 -0
  14. package/dist/cjs/events/recovery.d.ts +87 -0
  15. package/dist/cjs/events/recovery.js +281 -0
  16. package/dist/cjs/events/scanning.d.ts +35 -0
  17. package/dist/cjs/events/scanning.js +98 -0
  18. package/dist/cjs/events/tree.d.ts +27 -0
  19. package/dist/cjs/events/tree.js +63 -0
  20. package/dist/cjs/events/types.d.ts +39 -0
  21. package/dist/cjs/events/types.js +2 -0
  22. package/dist/cjs/events.d.ts +1 -77
  23. package/dist/cjs/events.js +6 -154
  24. package/dist/cjs/identity/encoding.d.ts +9 -0
  25. package/dist/cjs/identity/encoding.js +85 -0
  26. package/dist/cjs/identity/index.d.ts +4 -0
  27. package/dist/cjs/identity/index.js +20 -0
  28. package/dist/cjs/identity/noteKey.d.ts +33 -0
  29. package/dist/cjs/identity/noteKey.js +97 -0
  30. package/dist/cjs/identity/spending.d.ts +18 -0
  31. package/dist/cjs/identity/spending.js +46 -0
  32. package/dist/cjs/identity/viewKey.d.ts +38 -0
  33. package/dist/cjs/identity/viewKey.js +105 -0
  34. package/dist/cjs/idl/index.d.ts +18 -0
  35. package/dist/cjs/idl/index.js +24 -0
  36. package/dist/cjs/idl/privacy_pool.json +9907 -9907
  37. package/dist/cjs/idl/privacy_pool_legacy.d.ts +5 -0
  38. package/dist/cjs/idl/privacy_pool_legacy.js +3307 -0
  39. package/dist/cjs/idl/privacy_pool_legacy.json +2302 -0
  40. package/dist/cjs/idl/privacy_pool_legacy2.d.ts +5 -0
  41. package/dist/cjs/idl/privacy_pool_legacy2.js +3397 -0
  42. package/dist/cjs/idl/privacy_pool_legacy2.json +2376 -0
  43. package/dist/cjs/index.d.ts +8 -3
  44. package/dist/cjs/index.js +12 -2
  45. package/dist/cjs/notes/amount.d.ts +2 -0
  46. package/dist/cjs/notes/amount.js +21 -0
  47. package/dist/cjs/notes/index.d.ts +3 -0
  48. package/dist/cjs/notes/index.js +3 -0
  49. package/dist/cjs/notes/recovery.d.ts +16 -6
  50. package/dist/cjs/notes/recovery.js +33 -16
  51. package/dist/cjs/notes/selection.d.ts +77 -0
  52. package/dist/cjs/notes/selection.js +193 -0
  53. package/dist/cjs/notes/spent.d.ts +60 -0
  54. package/dist/cjs/notes/spent.js +101 -0
  55. package/dist/cjs/perps/constants.d.ts +16 -0
  56. package/dist/cjs/perps/constants.js +52 -0
  57. package/dist/cjs/perps/index.d.ts +5 -0
  58. package/dist/cjs/perps/index.js +21 -0
  59. package/dist/cjs/perps/keys.d.ts +16 -0
  60. package/dist/cjs/perps/keys.js +43 -0
  61. package/dist/cjs/perps/pdas.d.ts +29 -0
  62. package/dist/cjs/perps/pdas.js +55 -0
  63. package/dist/cjs/perps/relayer.d.ts +9 -0
  64. package/dist/cjs/perps/relayer.js +35 -0
  65. package/dist/cjs/perps/types.d.ts +126 -0
  66. package/dist/cjs/perps/types.js +2 -0
  67. package/dist/cjs/positions/index.d.ts +4 -0
  68. package/dist/cjs/positions/index.js +20 -0
  69. package/dist/cjs/positions/keys.d.ts +29 -0
  70. package/dist/cjs/positions/keys.js +71 -0
  71. package/dist/cjs/positions/pdas.d.ts +15 -0
  72. package/dist/cjs/positions/pdas.js +54 -0
  73. package/dist/cjs/positions/relayer.d.ts +4 -0
  74. package/dist/cjs/positions/relayer.js +15 -0
  75. package/dist/cjs/positions/types.d.ts +97 -0
  76. package/dist/cjs/positions/types.js +2 -0
  77. package/dist/cjs/predictions/client.d.ts +41 -0
  78. package/dist/cjs/predictions/client.js +147 -0
  79. package/dist/cjs/predictions/index.d.ts +5 -0
  80. package/dist/cjs/predictions/index.js +21 -0
  81. package/dist/cjs/predictions/keys.d.ts +14 -0
  82. package/dist/cjs/predictions/keys.js +40 -0
  83. package/dist/cjs/predictions/pdas.d.ts +6 -0
  84. package/dist/cjs/predictions/pdas.js +13 -0
  85. package/dist/cjs/predictions/relayer.d.ts +3 -0
  86. package/dist/cjs/predictions/relayer.js +11 -0
  87. package/dist/cjs/predictions/types.d.ts +178 -0
  88. package/dist/cjs/predictions/types.js +2 -0
  89. package/dist/cjs/program.d.ts +2 -0
  90. package/dist/cjs/program.js +8 -7
  91. package/dist/cjs/prover.d.ts +27 -4
  92. package/dist/cjs/prover.js +107 -48
  93. package/dist/cjs/relayer/api.d.ts +31 -0
  94. package/dist/cjs/relayer/api.js +86 -0
  95. package/dist/cjs/relayer/client.d.ts +29 -17
  96. package/dist/cjs/relayer/client.js +72 -17
  97. package/dist/cjs/relayer/errors.d.ts +1 -1
  98. package/dist/cjs/relayer/errors.js +1 -1
  99. package/dist/cjs/relayer/index.d.ts +4 -1
  100. package/dist/cjs/relayer/index.js +1 -1
  101. package/dist/cjs/relayer/internal-config.d.ts +36 -0
  102. package/dist/cjs/relayer/internal-config.js +9 -0
  103. package/dist/cjs/relayer/internal.d.ts +6 -0
  104. package/dist/cjs/relayer/internal.js +14 -0
  105. package/dist/cjs/relayer/transport.d.ts +4 -2
  106. package/dist/cjs/relayer/transport.js +10 -5
  107. package/dist/cjs/relayer/types.d.ts +9 -10
  108. package/dist/cjs/relayer/types.js +1 -1
  109. package/dist/cjs/shield/owner.js +1 -4
  110. package/dist/esm/events/index.d.ts +6 -0
  111. package/dist/esm/events/index.js +6 -0
  112. package/dist/esm/events/parsing.d.ts +25 -0
  113. package/dist/esm/events/parsing.js +120 -0
  114. package/dist/esm/events/position.d.ts +26 -0
  115. package/dist/esm/events/position.js +48 -0
  116. package/dist/esm/events/recovery.d.ts +87 -0
  117. package/dist/esm/events/recovery.js +237 -0
  118. package/dist/esm/events/scanning.d.ts +35 -0
  119. package/dist/esm/events/scanning.js +92 -0
  120. package/dist/esm/events/tree.d.ts +27 -0
  121. package/dist/esm/events/tree.js +58 -0
  122. package/dist/esm/events/types.d.ts +39 -0
  123. package/dist/esm/events/types.js +1 -0
  124. package/dist/esm/events.d.ts +1 -77
  125. package/dist/esm/events.js +3 -129
  126. package/dist/esm/identity/encoding.d.ts +9 -0
  127. package/dist/esm/identity/encoding.js +75 -0
  128. package/dist/esm/identity/index.d.ts +4 -0
  129. package/dist/esm/identity/index.js +4 -0
  130. package/dist/esm/identity/noteKey.d.ts +33 -0
  131. package/dist/esm/identity/noteKey.js +91 -0
  132. package/dist/esm/identity/spending.d.ts +18 -0
  133. package/dist/esm/identity/spending.js +35 -0
  134. package/dist/esm/identity/viewKey.d.ts +38 -0
  135. package/dist/esm/identity/viewKey.js +96 -0
  136. package/dist/esm/idl/index.d.ts +18 -0
  137. package/dist/esm/idl/index.js +18 -0
  138. package/dist/esm/idl/privacy_pool.json +9907 -9907
  139. package/dist/esm/idl/privacy_pool_legacy.d.ts +5 -0
  140. package/dist/esm/idl/privacy_pool_legacy.js +3305 -0
  141. package/dist/esm/idl/privacy_pool_legacy.json +2302 -0
  142. package/dist/esm/idl/privacy_pool_legacy2.d.ts +5 -0
  143. package/dist/esm/idl/privacy_pool_legacy2.js +3395 -0
  144. package/dist/esm/idl/privacy_pool_legacy2.json +2376 -0
  145. package/dist/esm/index.d.ts +8 -3
  146. package/dist/esm/index.js +8 -3
  147. package/dist/esm/notes/amount.d.ts +2 -0
  148. package/dist/esm/notes/amount.js +18 -0
  149. package/dist/esm/notes/index.d.ts +3 -0
  150. package/dist/esm/notes/index.js +3 -0
  151. package/dist/esm/notes/recovery.d.ts +16 -6
  152. package/dist/esm/notes/recovery.js +33 -16
  153. package/dist/esm/notes/selection.d.ts +77 -0
  154. package/dist/esm/notes/selection.js +188 -0
  155. package/dist/esm/notes/spent.d.ts +60 -0
  156. package/dist/esm/notes/spent.js +96 -0
  157. package/dist/esm/perps/constants.d.ts +16 -0
  158. package/dist/esm/perps/constants.js +48 -0
  159. package/dist/esm/perps/index.d.ts +5 -0
  160. package/dist/esm/perps/index.js +5 -0
  161. package/dist/esm/perps/keys.d.ts +16 -0
  162. package/dist/esm/perps/keys.js +35 -0
  163. package/dist/esm/perps/pdas.d.ts +29 -0
  164. package/dist/esm/perps/pdas.js +47 -0
  165. package/dist/esm/perps/relayer.d.ts +9 -0
  166. package/dist/esm/perps/relayer.js +25 -0
  167. package/dist/esm/perps/types.d.ts +126 -0
  168. package/dist/esm/perps/types.js +1 -0
  169. package/dist/esm/positions/index.d.ts +4 -0
  170. package/dist/esm/positions/index.js +4 -0
  171. package/dist/esm/positions/keys.d.ts +29 -0
  172. package/dist/esm/positions/keys.js +65 -0
  173. package/dist/esm/positions/pdas.d.ts +15 -0
  174. package/dist/esm/positions/pdas.js +44 -0
  175. package/dist/esm/positions/relayer.d.ts +4 -0
  176. package/dist/esm/positions/relayer.js +10 -0
  177. package/dist/esm/positions/types.d.ts +97 -0
  178. package/dist/esm/positions/types.js +1 -0
  179. package/dist/esm/predictions/client.d.ts +41 -0
  180. package/dist/esm/predictions/client.js +141 -0
  181. package/dist/esm/predictions/index.d.ts +5 -0
  182. package/dist/esm/predictions/index.js +5 -0
  183. package/dist/esm/predictions/keys.d.ts +14 -0
  184. package/dist/esm/predictions/keys.js +33 -0
  185. package/dist/esm/predictions/pdas.d.ts +6 -0
  186. package/dist/esm/predictions/pdas.js +10 -0
  187. package/dist/esm/predictions/relayer.d.ts +3 -0
  188. package/dist/esm/predictions/relayer.js +7 -0
  189. package/dist/esm/predictions/types.d.ts +178 -0
  190. package/dist/esm/predictions/types.js +1 -0
  191. package/dist/esm/program.d.ts +2 -0
  192. package/dist/esm/program.js +4 -3
  193. package/dist/esm/prover.d.ts +27 -4
  194. package/dist/esm/prover.js +106 -15
  195. package/dist/esm/relayer/api.d.ts +31 -0
  196. package/dist/esm/relayer/api.js +55 -0
  197. package/dist/esm/relayer/client.d.ts +29 -17
  198. package/dist/esm/relayer/client.js +72 -17
  199. package/dist/esm/relayer/errors.d.ts +1 -1
  200. package/dist/esm/relayer/errors.js +1 -1
  201. package/dist/esm/relayer/index.d.ts +4 -1
  202. package/dist/esm/relayer/index.js +1 -1
  203. package/dist/esm/relayer/internal-config.d.ts +36 -0
  204. package/dist/esm/relayer/internal-config.js +6 -0
  205. package/dist/esm/relayer/internal.d.ts +6 -0
  206. package/dist/esm/relayer/internal.js +10 -0
  207. package/dist/esm/relayer/transport.d.ts +4 -2
  208. package/dist/esm/relayer/transport.js +10 -5
  209. package/dist/esm/relayer/types.d.ts +9 -10
  210. package/dist/esm/relayer/types.js +1 -1
  211. package/dist/esm/shield/owner.js +1 -4
  212. package/identity.d.ts +2 -0
  213. package/identity.js +3 -0
  214. package/idl.d.ts +2 -0
  215. package/idl.js +3 -0
  216. package/notes.d.ts +2 -0
  217. package/notes.js +3 -0
  218. package/package.json +85 -3
  219. package/perps.d.ts +2 -0
  220. package/perps.js +3 -0
  221. package/poseidon.d.ts +2 -0
  222. package/poseidon.js +3 -0
  223. package/positions.d.ts +2 -0
  224. package/positions.js +3 -0
  225. package/predictions.d.ts +2 -0
  226. package/predictions.js +3 -0
  227. package/proof.d.ts +2 -0
  228. package/proof.js +3 -0
  229. package/prover.d.ts +2 -0
  230. package/prover.js +3 -0
  231. package/relayer.d.ts +2 -0
  232. package/relayer.js +3 -0
  233. package/shield.d.ts +2 -0
  234. package/shield.js +3 -0
  235. package/transactions.d.ts +2 -0
  236. package/transactions.js +3 -0
package/README.md CHANGED
@@ -6,6 +6,7 @@ The SDK gives TypeScript applications a safe, typed way to:
6
6
 
7
7
  - shield SOL or SPL tokens into Veilo;
8
8
  - withdraw, privately transfer, or privately swap shielded funds;
9
+ - open and manage private positions, Jupiter perps, and predictions;
9
10
  - work with Veilo's relayer without handling its encryption protocol yourself;
10
11
  - add partner-powered private sends and swaps through the Cloak API.
11
12
 
@@ -31,7 +32,7 @@ You will also need:
31
32
  - a Solana RPC connection;
32
33
  - a connected wallet when a user must sign;
33
34
  - the Veilo circuit `.wasm` and `.zkey` files for local proof generation;
34
- - a Veilo-issued API key for partner or relayer endpoints that require one.
35
+ - an API key only for partner services such as Cloak or Jupiter Predictions.
35
36
 
36
37
  ## Choose an integration
37
38
 
@@ -39,7 +40,7 @@ You will also need:
39
40
  | --- | --- | --- |
40
41
  | Let any wallet shield funds into Veilo | `shield()` | The user's wallet |
41
42
  | Add private transfers or swaps to a partner app | `VeiloCloakClient` | The user's wallet |
42
- | Build a full Veilo wallet experience | `VeiloRelayerClient` | The relayer for private spends |
43
+ | Build a full Veilo wallet experience | Managed relayer helpers | The relayer for private spends |
43
44
  | Manage proofs, notes, trees, and transactions yourself | Transaction helpers | Your application or relayer |
44
45
 
45
46
  For most third-party deposit integrations, `shield()` is the best starting
@@ -231,31 +232,194 @@ that fits your application:
231
232
  | Private transfer | `submitPrivateTransfer()` | `privateTransfer()` |
232
233
  | Private swap | `submitPrivateSwap()` | `buildPrivateSwapInstructions()` or `transactSwap()` |
233
234
 
234
- ### Use the relayer client
235
+ ### Use the managed relayer helpers
235
236
 
236
- `VeiloRelayerClient` encrypts private transaction requests before sending them
237
- to the relayer. The request and response types are exported for TypeScript
238
- autocomplete.
237
+ The SDK owns Veilo's production relayer URL, encryption key, request encryption,
238
+ and safe retry behavior. Applications call typed functions directly; there is
239
+ no relayer client or URL to configure.
239
240
 
240
241
  ```ts
241
242
  import {
242
- VeiloRelayerClient,
243
+ submitWithdraw,
243
244
  type WithdrawRequest,
244
245
  } from "@veilo/sdk-core/relayer";
245
246
 
246
- const relayer = new VeiloRelayerClient({
247
- apiKey: process.env.VEILO_RELAYER_KEY!,
248
- relayerPublicKey: process.env.VEILO_RELAYER_PUBLIC_KEY!,
249
- authToken: process.env.VEILO_AUTH_TOKEN,
250
- });
251
-
252
247
  async function submitWithdrawal(request: WithdrawRequest) {
253
- return relayer.submitWithdraw(request);
248
+ return submitWithdraw(request);
249
+ }
250
+ ```
251
+
252
+ The same focused entry point provides account authentication, encrypted note
253
+ storage, Merkle tree reads, private transfers, private swaps, and these product
254
+ flows:
255
+
256
+ | Product | Relayer helpers |
257
+ | --- | --- |
258
+ | Private positions | `submitOpenPosition()`, `submitClosePosition()`, `submitMergePositions()` |
259
+ | Jupiter perps | `submitJperpOpen()`, `submitJperpClose()`, TP/SL, cancellation, recovery, and reissue helpers |
260
+ | Jupiter predictions | `submitPredictionOpen()` and `submitPredictionReissue()` |
261
+
262
+ #### Submission always targets Veilo's relayer
263
+
264
+ Every helper above submits to Veilo's relayer. There is no `baseUrl` option, no
265
+ client to construct, and no supported way to redirect submission. That is
266
+ deliberate, for two independent reasons:
267
+
268
+ 1. **The program requires it.** The privacy pool checks the submitting relayer
269
+ against an on-chain allowlist on every spend. Deposits (`public_amount > 0`)
270
+ are permissionless — which is why `shield()` and `deposit()` work with any
271
+ wallet — but withdrawals, transfers, swaps, positions, perps, and predictions
272
+ all fail with `RelayerNotAllowed` unless the transaction is signed by a
273
+ whitelisted relayer. Pointing the SDK at a different host would produce a
274
+ well-formed request that cannot land on chain.
275
+ 2. **These requests carry spending keys.** `TransactNote.privateKey`, and the
276
+ `claimantSecretKey` on position close and merge, *are* the spend authority for
277
+ the funds involved (see [A private note is valuable secret
278
+ data](#a-private-note-is-valuable-secret-data)). A configurable submission
279
+ target would be a configurable destination for user funds.
280
+
281
+ Running your own relayer is a whitelisting conversation rather than a
282
+ configuration flag — see [Support](#support). Reaching past the package's
283
+ `exports` map to import internal modules is unsupported and not covered by
284
+ semver.
285
+
286
+ ### Spend status and private balance
287
+
288
+ Nothing in a note says whether it has been spent — a note is yours until its
289
+ nullifier is published. So a balance is always two steps, and the SDK gives you
290
+ both:
291
+
292
+ ```ts
293
+ import { getPrivateBalance, checkNullifiersSpent } from "@veilo/sdk-core";
294
+
295
+ const { total, byMint, unspent, spent } = await getPrivateBalance(myNotes);
296
+ ```
297
+
298
+ Each note needs an `amount`, a 64-character hex `nullifier`, and optionally a
299
+ `mint` to group by. `getPrivateBalance` asks the relayer which nullifiers are
300
+ spent, then sums what is left; `unspent` and `spent` hand back your own note
301
+ objects, so you can render from them directly.
302
+
303
+ `checkNullifiersSpent(nullifiers)` is the same question on its own. It
304
+ deduplicates, splits into the server's 200-per-request batches, issues them
305
+ concurrently, and returns a `Set` for membership testing. It also lower-cases
306
+ input: the endpoint validates case-insensitively but answers in lower case, so
307
+ upper-case hex would otherwise never match and every note would read as unspent.
308
+
309
+ To compute a balance without the relayer — from a chain scan of nullifier
310
+ events, or a local cache — supply the answer yourself:
311
+
312
+ ```ts
313
+ await getPrivateBalance(myNotes, { resolveSpent: async (ns) => mySpentSet(ns) });
314
+ ```
315
+
316
+ Amounts stay `bigint` end to end. A `number` amount above `Number.MAX_SAFE_INTEGER`
317
+ is rejected rather than silently rounded, and a malformed nullifier throws
318
+ instead of counting as unspent — either would overstate a balance.
319
+
320
+ ### Choosing notes to spend
321
+
322
+ Two rules govern this, and both are easy to get wrong:
323
+
324
+ - the transaction circuit is **2-in-2-out**, so one transaction spends at most
325
+ two notes;
326
+ - notes are only co-spendable inside the **same Merkle tree**, and the tree is
327
+ derived from the mint.
328
+
329
+ ```ts
330
+ import { selectNotesForAmount, canonicalTreeId } from "@veilo/sdk-core";
331
+
332
+ const selection = selectNotesForAmount(unspentNotes, amountRaw, { mint });
333
+ if (!selection.ok) throw new Error(selection.message); // NO_NOTES | INSUFFICIENT_FUNDS
334
+ if (selection.requiresMerge) {
335
+ // more than two inputs — combine them before proving
254
336
  }
255
337
  ```
256
338
 
257
- The same client also provides account authentication, encrypted note storage,
258
- Merkle tree reads, private transfers, and private swaps.
339
+ It searches each tree independently and returns the best result: the smallest
340
+ single note that covers the amount, else the pair with the least change (found
341
+ by a two-pointer scan, not an all-pairs search), else the largest notes greedily
342
+ with `requiresMerge` set. A failure reports `available`, the largest total
343
+ reachable within one tree.
344
+
345
+ `canonicalTreeId(mint)` derives a note's tree the way the relayer does. **Derive
346
+ it; never trust a stored `treeId`.** Change notes are written with the on-chain
347
+ shard index while deposits and synced notes carry the mint-derived id — for
348
+ native SOL both are 0 so the difference hides, but for SPL tokens they diverge
349
+ and co-spending across the two fails at proof time as a commitment mismatch.
350
+
351
+ ### Private product keys and recovery
352
+
353
+ Private positions, perps, and predictions use deterministic client-held keys.
354
+ The SDK provides the exact derivations used by Veilo's wallet, extension,
355
+ relayer, and program tests. Keep the spending key and claimant secret on the
356
+ user's device; send claimant secrets only inside the SDK's encrypted relayer
357
+ requests.
358
+
359
+ ```ts
360
+ import {
361
+ derivePositionKeyBundle,
362
+ deriveSpendingKeyWithSigner,
363
+ encodeClaimantSecretKey,
364
+ initPoseidon,
365
+ submitOpenPosition,
366
+ } from "@veilo/sdk-core";
367
+
368
+ await initPoseidon();
369
+ const spendingKey = await deriveSpendingKeyWithSigner(
370
+ (message) => wallet.signMessage(message),
371
+ );
372
+ const positionKeys = derivePositionKeyBundle(spendingKey, nextPositionIndex);
373
+
374
+ await submitOpenPosition({
375
+ // Select private source notes in your wallet before calling the relayer.
376
+ notes,
377
+ sourceMintAddress,
378
+ destMintAddress,
379
+ swapAmountRaw,
380
+ slippageBps: 50,
381
+ userPublicKey: wallet.publicKey.toBase58(),
382
+ veiloPublicKey,
383
+ position: positionKeys.position,
384
+ });
385
+
386
+ // Persist the returned position data and `nextPositionIndex`. Re-derive the
387
+ // same claimant later when closing or merging the position.
388
+ const claimantSecretKey = encodeClaimantSecretKey(positionKeys.claimant);
389
+ ```
390
+
391
+ Use `deriveJperpKeyBundle()` and `getJperpMarketPayload()` for private perps.
392
+ Use `derivePredictionKeyBundle()` for private predictions. Lower-level key
393
+ derivations are also exported when an application needs them individually. The
394
+ PDA helpers in each focused entry point support on-chain recovery scans on a
395
+ fresh device.
396
+
397
+ Predictions have one additional step: Jupiter returns an unsigned order,
398
+ close, or claim transaction. The SDK exposes that API without taking custody
399
+ of signing:
400
+
401
+ ```ts
402
+ import {
403
+ JupiterPredictionClient,
404
+ deserializePredictionTransaction,
405
+ } from "@veilo/sdk-core/predictions";
406
+
407
+ const predictions = new JupiterPredictionClient({ apiKey: jupiterApiKey });
408
+ const order = await predictions.placeOrder({
409
+ ownerPubkey: ephemeral.publicKey.toBase58(),
410
+ marketId,
411
+ isYes: true,
412
+ isBuy: true,
413
+ depositAmount: "5000000",
414
+ depositMint: usdcMint,
415
+ });
416
+ const transaction = deserializePredictionTransaction(order);
417
+ // Ask the deterministic ephemeral wallet to sign, then submit and confirm it.
418
+ ```
419
+
420
+ The SDK deliberately does not own application storage or choose which notes to
421
+ spend. Persist indexes and returned product records only after the corresponding
422
+ transaction is confirmed.
259
423
 
260
424
  ### Use the direct helpers
261
425
 
@@ -347,8 +511,28 @@ for shields and call a deposit's `commit()` callback only after confirmation.
347
511
  ### Proof files are separate
348
512
 
349
513
  Circuit `.wasm` and `.zkey` files are intentionally not bundled with the npm
350
- package because they are large. Provide local paths, byte arrays, or hosted URLs
351
- to `createTransactionProver()` and `createSwapProver()`.
514
+ package because they are large `files` ships `dist/` only. Provide local
515
+ paths, byte arrays, or hosted URLs to `createTransactionProver()` and
516
+ `createSwapProver()`.
517
+
518
+ Both arguments are optional. Omit them and the prover resolves artifacts from
519
+ disk on first use, first hit wins:
520
+
521
+ 1. `$VEILO_CIRCUITS_DIR`
522
+ 2. `<package>/circuits` — populated by `npm run copy:circuits`
523
+ 3. `<cwd>/circuits`
524
+ 4. `<cwd>/node_modules/@veilo/sdk-core/circuits`
525
+
526
+ ```ts
527
+ const prover = createTransactionProver(); // resolved from disk
528
+ const prover = createTransactionProver({ wasmPath, zkeyPath }); // explicit
529
+ ```
530
+
531
+ `resolveCircuitArtifacts("transaction" | "swap")` performs that lookup on its
532
+ own if you want the paths. Resolution is **Node-only and lazy** — it imports
533
+ `node:fs` inside the call so browser bundles can still import this module, and
534
+ there is nothing to resolve in a browser anyway. Browser builds must pass
535
+ `CircuitArtifacts` explicitly. A miss throws with every path it tried.
352
536
 
353
537
  ## Error handling
354
538
 
@@ -388,8 +572,11 @@ import { shield } from "@veilo/sdk-core";
388
572
  import { getPoolPdas } from "@veilo/sdk-core/accounts";
389
573
  import { createUTXO } from "@veilo/sdk-core/notes";
390
574
  import { deposit, withdraw } from "@veilo/sdk-core/transactions";
391
- import { VeiloRelayerClient } from "@veilo/sdk-core/relayer";
575
+ import { submitWithdraw } from "@veilo/sdk-core/relayer";
392
576
  import { VeiloCloakClient } from "@veilo/sdk-core/cloak";
577
+ import { derivePositionKeyBundle } from "@veilo/sdk-core/positions";
578
+ import { getJperpMarketPayload } from "@veilo/sdk-core/perps";
579
+ import { JupiterPredictionClient } from "@veilo/sdk-core/predictions";
393
580
  ```
394
581
 
395
582
  Available focused entry points:
@@ -397,7 +584,12 @@ Available focused entry points:
397
584
  - `@veilo/sdk-core/accounts`
398
585
  - `@veilo/sdk-core/cloak`
399
586
  - `@veilo/sdk-core/config`
587
+ - `@veilo/sdk-core/idl`
588
+ - `@veilo/sdk-core/identity`
400
589
  - `@veilo/sdk-core/notes`
590
+ - `@veilo/sdk-core/perps`
591
+ - `@veilo/sdk-core/positions`
592
+ - `@veilo/sdk-core/predictions`
401
593
  - `@veilo/sdk-core/poseidon`
402
594
  - `@veilo/sdk-core/proof`
403
595
  - `@veilo/sdk-core/prover`
@@ -421,6 +613,32 @@ or relayer:
421
613
  These APIs are fully typed. Use their TypeScript definitions as the source of
422
614
  truth for required inputs.
423
615
 
616
+ ### Historical IDLs and commitment recovery
617
+
618
+ The SDK includes the current, legacy, and legacy-2 privacy-pool IDLs used by the
619
+ relayer. Event scans automatically try the historical layouts, so applications
620
+ can rebuild trees across program upgrades.
621
+
622
+ ```ts
623
+ import {
624
+ PRIVACY_POOL_IDLS,
625
+ buildTreeFromEvents,
626
+ resolveCommitmentEvents,
627
+ } from "@veilo/sdk-core";
628
+ ```
629
+
630
+ `buildTreeFromEvents()` includes Veilo's protected historical repairs by
631
+ default and rejects gaps that would produce an incorrect root. When a confirmed
632
+ instruction appended commitments without emitting `CommitmentEvent`, use
633
+ `resolveCommitmentEvents()` with the expected commitments and current local
634
+ tree. It prefers emitted metadata and computes only the missing entries.
635
+
636
+ For the deployed eventless `open_position` path, use
637
+ `recoverEventlessOpenPositionEvents()`. It decodes both commitments from the
638
+ instruction and reads the authoritative global position-tree leaf index from
639
+ the program-owned PositionPDA. `buildGlobalPositionTreeFromEvents()` mirrors
640
+ the relayer's all-mint position-tree reconstruction.
641
+
424
642
  ## Development
425
643
 
426
644
  ```bash
package/accounts.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ export * from "./dist/cjs/accounts/index";
package/accounts.js ADDED
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ // Resolves "@veilo/sdk-core/accounts" for legacy moduleResolution: "node".
3
+ module.exports = require("./dist/cjs/accounts/index.js");
package/cloak.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ export * from "./dist/cjs/cloak";
package/cloak.js ADDED
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ // Resolves "@veilo/sdk-core/cloak" for legacy moduleResolution: "node".
3
+ module.exports = require("./dist/cjs/cloak.js");
package/config.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ export * from "./dist/cjs/config";
package/config.js ADDED
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/gen-subpath-stubs.mjs — do not edit.
2
+ // Resolves "@veilo/sdk-core/config" for legacy moduleResolution: "node".
3
+ module.exports = require("./dist/cjs/config.js");
@@ -0,0 +1,6 @@
1
+ export * from "./types.js";
2
+ export * from "./parsing.js";
3
+ export * from "./position.js";
4
+ export * from "./recovery.js";
5
+ export * from "./scanning.js";
6
+ export * from "./tree.js";
@@ -0,0 +1,22 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types.js"), exports);
18
+ __exportStar(require("./parsing.js"), exports);
19
+ __exportStar(require("./position.js"), exports);
20
+ __exportStar(require("./recovery.js"), exports);
21
+ __exportStar(require("./scanning.js"), exports);
22
+ __exportStar(require("./tree.js"), exports);
@@ -0,0 +1,25 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { type VersionedPrivacyPoolIdl } from "../idl/index.js";
3
+ import type { CommitmentEvent, NullifierSpentEvent, ParsedVeiloEvent } from "./types.js";
4
+ /**
5
+ * Decode logs using the current, legacy, and legacy-2 IDLs.
6
+ *
7
+ * IDLs are evaluated in the relayer's current/legacy/legacy-2 order. Anchor's
8
+ * Borsh decoder accepts trailing bytes, however, so every successful parse is
9
+ * considered and the richest result wins. That preserves the compact fields
10
+ * in legacy-2 events instead of misclassifying them as the shorter legacy form.
11
+ * Ties retain the relayer's order.
12
+ */
13
+ export declare function parseVeiloEventsWithFallback(logMessages: readonly string[] | null | undefined, programId: PublicKey, idls?: readonly VersionedPrivacyPoolIdl[]): ParsedVeiloEvent[];
14
+ export declare function parseCommitmentEventsFromLogs(params: {
15
+ logMessages: readonly string[] | null | undefined;
16
+ programId: PublicKey;
17
+ txSignature: string;
18
+ slot: number;
19
+ }): CommitmentEvent[];
20
+ export declare function parseNullifierEventsFromLogs(params: {
21
+ logMessages: readonly string[] | null | undefined;
22
+ programId: PublicKey;
23
+ txSignature: string;
24
+ slot: number;
25
+ }): NullifierSpentEvent[];
@@ -0,0 +1,158 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseVeiloEventsWithFallback = parseVeiloEventsWithFallback;
37
+ exports.parseCommitmentEventsFromLogs = parseCommitmentEventsFromLogs;
38
+ exports.parseNullifierEventsFromLogs = parseNullifierEventsFromLogs;
39
+ const anchor = __importStar(require("@coral-xyz/anchor"));
40
+ const web3_js_1 = require("@solana/web3.js");
41
+ const index_js_1 = require("../idl/index.js");
42
+ /**
43
+ * Decode logs using the current, legacy, and legacy-2 IDLs.
44
+ *
45
+ * IDLs are evaluated in the relayer's current/legacy/legacy-2 order. Anchor's
46
+ * Borsh decoder accepts trailing bytes, however, so every successful parse is
47
+ * considered and the richest result wins. That preserves the compact fields
48
+ * in legacy-2 events instead of misclassifying them as the shorter legacy form.
49
+ * Ties retain the relayer's order.
50
+ */
51
+ function parseVeiloEventsWithFallback(logMessages, programId, idls = index_js_1.PRIVACY_POOL_IDLS) {
52
+ if (!logMessages?.length)
53
+ return [];
54
+ let best = [];
55
+ let bestScore = -1;
56
+ for (const candidate of idls) {
57
+ try {
58
+ const parser = new anchor.EventParser(programId, new anchor.BorshCoder(candidate.idl));
59
+ const parsed = Array.from(parser.parseLogs([...logMessages])).map((event) => ({
60
+ name: event.name,
61
+ data: event.data,
62
+ idlVersion: candidate.version,
63
+ }));
64
+ const score = parsed.reduce((total, event) => total + decodedFieldCount(event.data), 0);
65
+ if (score > bestScore) {
66
+ best = parsed;
67
+ bestScore = score;
68
+ }
69
+ }
70
+ catch {
71
+ // The discriminator is shared while historical payload lengths differ.
72
+ }
73
+ }
74
+ return best;
75
+ }
76
+ function parseCommitmentEventsFromLogs(params) {
77
+ const { logMessages, programId, txSignature, slot } = params;
78
+ const out = [];
79
+ for (const event of parseVeiloEventsWithFallback(logMessages, programId)) {
80
+ if (event.name.toLowerCase() !== "commitmentevent")
81
+ continue;
82
+ const data = event.data;
83
+ const mintAddress = toPublicKey(field(data, "mintAddress", "mint_address"));
84
+ const ephemeralPublicKey = optionalBytes(field(data, "ephemeralPublicKey", "ephemeral_public_key"));
85
+ const encryptedBlob = optionalBytes(field(data, "encryptedBlob", "encrypted_blob"));
86
+ const rawViewTag = field(data, "viewTag", "view_tag");
87
+ out.push({
88
+ commitment: toBytes(field(data, "commitment"), 32, "commitment"),
89
+ leafIndex: toSafeNumber(field(data, "leafIndex", "leaf_index"), "leafIndex"),
90
+ newRoot: toBytes(field(data, "newRoot", "new_root"), 32, "newRoot"),
91
+ timestamp: toSafeNumber(field(data, "timestamp"), "timestamp"),
92
+ mintAddress,
93
+ treeId: toSafeNumber(field(data, "treeId", "tree_id"), "treeId"),
94
+ txSignature,
95
+ slot,
96
+ ...(ephemeralPublicKey ? { ephemeralPublicKey } : {}),
97
+ ...(encryptedBlob ? { encryptedBlob } : {}),
98
+ ...(rawViewTag !== undefined
99
+ ? { viewTag: toSafeNumber(rawViewTag, "viewTag") }
100
+ : {}),
101
+ source: "emitted",
102
+ idlVersion: event.idlVersion,
103
+ });
104
+ }
105
+ return out;
106
+ }
107
+ function parseNullifierEventsFromLogs(params) {
108
+ const { logMessages, programId, txSignature, slot } = params;
109
+ const out = [];
110
+ for (const event of parseVeiloEventsWithFallback(logMessages, programId)) {
111
+ if (event.name.toLowerCase() !== "nullifierspent")
112
+ continue;
113
+ const data = event.data;
114
+ out.push({
115
+ nullifier: toBytes(field(data, "nullifier"), 32, "nullifier"),
116
+ timestamp: toSafeNumber(field(data, "timestamp"), "timestamp"),
117
+ mintAddress: toPublicKey(field(data, "mintAddress", "mint_address")),
118
+ treeId: toSafeNumber(field(data, "treeId", "tree_id"), "treeId"),
119
+ txSignature,
120
+ slot,
121
+ idlVersion: event.idlVersion,
122
+ });
123
+ }
124
+ return out;
125
+ }
126
+ function field(data, camel, snake) {
127
+ return data[camel] ?? (snake ? data[snake] : undefined);
128
+ }
129
+ function optionalBytes(value) {
130
+ return value === undefined || value === null
131
+ ? undefined
132
+ : new Uint8Array(value);
133
+ }
134
+ function decodedFieldCount(value) {
135
+ return value && typeof value === "object" ? Object.keys(value).length : 0;
136
+ }
137
+ function toBytes(value, length, label) {
138
+ const bytes = new Uint8Array(value);
139
+ if (bytes.length !== length) {
140
+ throw new Error(`${label} must be ${length} bytes, got ${bytes.length}`);
141
+ }
142
+ return bytes;
143
+ }
144
+ function toPublicKey(value) {
145
+ if (value instanceof web3_js_1.PublicKey)
146
+ return value;
147
+ return new web3_js_1.PublicKey(value);
148
+ }
149
+ function toSafeNumber(value, label) {
150
+ if (value === undefined || value === null) {
151
+ throw new Error(`Missing ${label} in decoded event`);
152
+ }
153
+ const numeric = typeof value === "number" ? value : Number(value);
154
+ if (!Number.isSafeInteger(numeric)) {
155
+ throw new Error(`${label} is outside JavaScript's safe integer range`);
156
+ }
157
+ return numeric;
158
+ }
@@ -0,0 +1,26 @@
1
+ import type { Idl, Program } from "@coral-xyz/anchor";
2
+ import { PublicKey } from "@solana/web3.js";
3
+ import type { CommitmentEvent } from "./types.js";
4
+ /**
5
+ * Privacy-pool mints copied from the relayer's global position-tree classifier.
6
+ * Position commitments use non-pool destination mints at the same tree ID.
7
+ */
8
+ export declare const POSITION_TREE_POOL_MINTS: readonly PublicKey[];
9
+ export declare function isGlobalPositionCommitment(event: CommitmentEvent): boolean;
10
+ /**
11
+ * Rebuild the one global position tree shared across every position mint.
12
+ * This mirrors the relayer: scan all mints for the position tree ID, then drop
13
+ * normal privacy-pool mint events before sorting by the global leaf index.
14
+ */
15
+ export declare function buildGlobalPositionTreeFromEvents<T extends Idl>(params: {
16
+ program: Program<T>;
17
+ positionTreeId?: number;
18
+ depth?: number;
19
+ additionalEvents?: readonly CommitmentEvent[];
20
+ includeProtectedEvents?: boolean;
21
+ strictLeafIndices?: boolean;
22
+ }): Promise<{
23
+ tree: import("../merkle.js").MerkleTree;
24
+ events: CommitmentEvent[];
25
+ latestSignature: string;
26
+ }>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POSITION_TREE_POOL_MINTS = void 0;
4
+ exports.isGlobalPositionCommitment = isGlobalPositionCommitment;
5
+ exports.buildGlobalPositionTreeFromEvents = buildGlobalPositionTreeFromEvents;
6
+ const web3_js_1 = require("@solana/web3.js");
7
+ const recovery_js_1 = require("./recovery.js");
8
+ const scanning_js_1 = require("./scanning.js");
9
+ const tree_js_1 = require("./tree.js");
10
+ /**
11
+ * Privacy-pool mints copied from the relayer's global position-tree classifier.
12
+ * Position commitments use non-pool destination mints at the same tree ID.
13
+ */
14
+ exports.POSITION_TREE_POOL_MINTS = [
15
+ web3_js_1.PublicKey.default,
16
+ new web3_js_1.PublicKey("So11111111111111111111111111111111111111112"),
17
+ new web3_js_1.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
18
+ new web3_js_1.PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"),
19
+ new web3_js_1.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"),
20
+ new web3_js_1.PublicKey("JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"),
21
+ new web3_js_1.PublicKey("ARXwZkNAtzPfdcoqQiduJn8EPv9fKiDfGn2KyggyDrFs"),
22
+ ];
23
+ const POOL_MINTS = new Set(exports.POSITION_TREE_POOL_MINTS.map((mint) => mint.toBase58()));
24
+ function isGlobalPositionCommitment(event) {
25
+ return !POOL_MINTS.has(event.mintAddress.toBase58());
26
+ }
27
+ /**
28
+ * Rebuild the one global position tree shared across every position mint.
29
+ * This mirrors the relayer: scan all mints for the position tree ID, then drop
30
+ * normal privacy-pool mint events before sorting by the global leaf index.
31
+ */
32
+ async function buildGlobalPositionTreeFromEvents(params) {
33
+ const { program, positionTreeId = 0, depth = 22, additionalEvents = [], includeProtectedEvents = true, strictLeafIndices = true, } = params;
34
+ const emitted = await (0, scanning_js_1.scanAllCommitmentEvents)({ program });
35
+ const protectedEvents = includeProtectedEvents
36
+ ? (0, recovery_js_1.getAllProtectedCommitmentEvents)()
37
+ : [];
38
+ const events = (0, tree_js_1.dedupeCommitmentEvents)([
39
+ ...emitted,
40
+ ...additionalEvents,
41
+ ...protectedEvents,
42
+ ])
43
+ .filter((event) => event.treeId === positionTreeId && isGlobalPositionCommitment(event))
44
+ .sort((a, b) => a.leafIndex - b.leafIndex);
45
+ return {
46
+ tree: (0, tree_js_1.reconstructTreeFromCommitmentEvents)(events, {
47
+ depth,
48
+ strictLeafIndices,
49
+ }),
50
+ events,
51
+ latestSignature: events[events.length - 1]?.txSignature,
52
+ };
53
+ }