@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
@@ -1,129 +1,3 @@
1
- import * as anchor from "@coral-xyz/anchor";
2
- import { MerkleTree } from "./merkle.js";
3
- // -----------------------------------------------------------------------------
4
- // Event Scanning
5
- // -----------------------------------------------------------------------------
6
- /**
7
- * Fetch and parse CommitmentEvents for a program, optionally filtered by mint.
8
- *
9
- * @param program - Anchor program instance
10
- * @param mintAddress - Filter to a specific pool; omit to get all pools
11
- * @param before - Start paging before this signature (exclusive)
12
- * @param until - Stop paging at this signature (exclusive)
13
- * @param limit - Max signatures to fetch per page (default 1000)
14
- */
15
- export async function scanCommitmentEvents(params) {
16
- const { program, mintAddress, before, until, limit = 1000 } = params;
17
- const out = [];
18
- const eventParser = new anchor.EventParser(program.programId, new anchor.BorshCoder(program.idl));
19
- const sigs = await program.provider.connection.getSignaturesForAddress(program.programId, { before, until, limit });
20
- for (const sigInfo of sigs) {
21
- if (sigInfo.err)
22
- continue;
23
- const tx = await program.provider.connection.getTransaction(sigInfo.signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0 });
24
- if (!tx)
25
- continue;
26
- const events = Array.from(eventParser.parseLogs(tx.meta?.logMessages ?? []));
27
- for (const ev of events) {
28
- if (ev.name !== "commitmentEvent")
29
- continue;
30
- const d = ev.data;
31
- const evMint = d.mintAddress;
32
- if (mintAddress && !evMint.equals(mintAddress))
33
- continue;
34
- out.push({
35
- commitment: new Uint8Array(d.commitment),
36
- leafIndex: Number(d.leafIndex),
37
- newRoot: new Uint8Array(d.newRoot),
38
- timestamp: Number(d.timestamp),
39
- mintAddress: evMint,
40
- treeId: Number(d.treeId),
41
- txSignature: sigInfo.signature,
42
- slot: sigInfo.slot,
43
- });
44
- }
45
- }
46
- return out;
47
- }
48
- /**
49
- * Fetch and parse NullifierSpent events for a program, optionally filtered by mint.
50
- */
51
- export async function scanNullifierEvents(params) {
52
- const { program, mintAddress, before, until, limit = 1000 } = params;
53
- const out = [];
54
- const eventParser = new anchor.EventParser(program.programId, new anchor.BorshCoder(program.idl));
55
- const sigs = await program.provider.connection.getSignaturesForAddress(program.programId, { before, until, limit });
56
- for (const sigInfo of sigs) {
57
- if (sigInfo.err)
58
- continue;
59
- const tx = await program.provider.connection.getTransaction(sigInfo.signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0 });
60
- if (!tx)
61
- continue;
62
- const events = Array.from(eventParser.parseLogs(tx.meta?.logMessages ?? []));
63
- for (const ev of events) {
64
- if (ev.name !== "nullifierSpent")
65
- continue;
66
- const d = ev.data;
67
- const evMint = d.mintAddress;
68
- if (mintAddress && !evMint.equals(mintAddress))
69
- continue;
70
- out.push({
71
- nullifier: new Uint8Array(d.nullifier),
72
- timestamp: Number(d.timestamp),
73
- mintAddress: evMint,
74
- treeId: Number(d.treeId),
75
- txSignature: sigInfo.signature,
76
- slot: sigInfo.slot,
77
- });
78
- }
79
- }
80
- return out;
81
- }
82
- // -----------------------------------------------------------------------------
83
- // Tree Reconstruction
84
- // -----------------------------------------------------------------------------
85
- /**
86
- * Rebuild a local MerkleTree from on-chain CommitmentEvents.
87
- * Use this to get a valid inclusion proof for spending any UTXO.
88
- *
89
- * CommitmentEvents are sorted by (treeId, leafIndex) so the local tree
90
- * matches the on-chain state exactly.
91
- *
92
- * @param program - Anchor program instance
93
- * @param mintAddress - Pool to reconstruct
94
- * @param treeId - Which tree to reconstruct (default 0)
95
- * @param depth - Merkle tree depth (default 22, must match on-chain)
96
- */
97
- export async function buildTreeFromEvents(params) {
98
- const { program, mintAddress, treeId = 0, depth = 22 } = params;
99
- // Fetch all commitment events for this pool (page until no more results).
100
- // Oldest-first so we insert leaves in the correct order.
101
- const allEvents = [];
102
- let before;
103
- while (true) {
104
- const page = await scanCommitmentEvents({
105
- program,
106
- mintAddress,
107
- before,
108
- limit: 1000,
109
- });
110
- if (page.length === 0)
111
- break;
112
- allEvents.push(...page);
113
- before = page[page.length - 1].txSignature;
114
- if (page.length < 1000)
115
- break;
116
- }
117
- // Keep only events for this treeId, sort by leafIndex ascending
118
- const treeEvents = allEvents
119
- .filter((e) => e.treeId === treeId)
120
- .sort((a, b) => a.leafIndex - b.leafIndex);
121
- const tree = new MerkleTree(depth);
122
- for (const ev of treeEvents) {
123
- tree.insert(ev.commitment);
124
- }
125
- const latestSignature = treeEvents.length > 0
126
- ? treeEvents[treeEvents.length - 1].txSignature
127
- : undefined;
128
- return { tree, events: treeEvents, latestSignature };
129
- }
1
+ // Backward-compatible entry point. Event parsing, scanning, and recovery live
2
+ // in focused modules under src/events.
3
+ export * from "./events/index.js";
@@ -0,0 +1,9 @@
1
+ export declare function concatBytes(...parts: readonly Uint8Array[]): Uint8Array;
2
+ export declare function utf8(value: string): Uint8Array;
3
+ export declare function uint32Le(value: number, label?: string): Uint8Array;
4
+ export declare function uint64Le(value: bigint, label?: string): Uint8Array;
5
+ export declare function assertBytes(value: Uint8Array, length: number, label: string): void;
6
+ export declare function bytesToHex(bytes: Uint8Array): string;
7
+ export declare function hexToBytes(value: string, length?: number, label?: string): Uint8Array;
8
+ /** Encode bytes using the Bitcoin/Solana base58 alphabet. */
9
+ export declare function encodeBase58(bytes: Uint8Array): string;
@@ -0,0 +1,75 @@
1
+ const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2
+ export function concatBytes(...parts) {
3
+ const output = new Uint8Array(parts.reduce((length, part) => length + part.length, 0));
4
+ let offset = 0;
5
+ for (const part of parts) {
6
+ output.set(part, offset);
7
+ offset += part.length;
8
+ }
9
+ return output;
10
+ }
11
+ export function utf8(value) {
12
+ return new TextEncoder().encode(value);
13
+ }
14
+ export function uint32Le(value, label = "value") {
15
+ if (!Number.isSafeInteger(value) || value < 0 || value > 4294967295) {
16
+ throw new RangeError(`${label} must be an unsigned 32-bit integer`);
17
+ }
18
+ const bytes = new Uint8Array(4);
19
+ new DataView(bytes.buffer).setUint32(0, value, true);
20
+ return bytes;
21
+ }
22
+ export function uint64Le(value, label = "value") {
23
+ if (value < 0n || value > 0xffffffffffffffffn) {
24
+ throw new RangeError(`${label} must be an unsigned 64-bit integer`);
25
+ }
26
+ const bytes = new Uint8Array(8);
27
+ new DataView(bytes.buffer).setBigUint64(0, value, true);
28
+ return bytes;
29
+ }
30
+ export function assertBytes(value, length, label) {
31
+ if (value.length !== length) {
32
+ throw new RangeError(`${label} must be ${length} bytes, got ${value.length}`);
33
+ }
34
+ }
35
+ export function bytesToHex(bytes) {
36
+ return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
37
+ }
38
+ export function hexToBytes(value, length, label = "hex value") {
39
+ const clean = value.startsWith("0x") ? value.slice(2) : value;
40
+ if (!/^[0-9a-f]*$/i.test(clean) || clean.length % 2 !== 0) {
41
+ throw new TypeError(`${label} must be an even-length hexadecimal string`);
42
+ }
43
+ const bytes = Uint8Array.from({ length: clean.length / 2 }, (_, index) => Number.parseInt(clean.slice(index * 2, index * 2 + 2), 16));
44
+ if (length !== undefined)
45
+ assertBytes(bytes, length, label);
46
+ return bytes;
47
+ }
48
+ /** Encode bytes using the Bitcoin/Solana base58 alphabet. */
49
+ export function encodeBase58(bytes) {
50
+ if (bytes.length === 0)
51
+ return "";
52
+ let zeroes = 0;
53
+ while (zeroes < bytes.length && bytes[zeroes] === 0)
54
+ zeroes++;
55
+ if (zeroes === bytes.length)
56
+ return "1".repeat(zeroes);
57
+ const digits = [0];
58
+ for (let index = zeroes; index < bytes.length; index++) {
59
+ let carry = bytes[index];
60
+ for (let digit = 0; digit < digits.length; digit++) {
61
+ const value = digits[digit] * 256 + carry;
62
+ digits[digit] = value % 58;
63
+ carry = Math.floor(value / 58);
64
+ }
65
+ while (carry > 0) {
66
+ digits.push(carry % 58);
67
+ carry = Math.floor(carry / 58);
68
+ }
69
+ }
70
+ let encoded = "1".repeat(zeroes);
71
+ for (let index = digits.length - 1; index >= 0; index--) {
72
+ encoded += BASE58_ALPHABET[digits[index]];
73
+ }
74
+ return encoded;
75
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./encoding.js";
2
+ export * from "./spending.js";
3
+ export * from "./noteKey.js";
4
+ export * from "./viewKey.js";
@@ -0,0 +1,4 @@
1
+ export * from "./encoding.js";
2
+ export * from "./spending.js";
3
+ export * from "./noteKey.js";
4
+ export * from "./viewKey.js";
@@ -0,0 +1,33 @@
1
+ /** Domain-separation label. Permanent — see the header. */
2
+ export declare const VEILO_NOTE_KEY_LABEL = "veilo_note_key_v1";
3
+ /** The note spending key plus the Poseidon public key derived from it. */
4
+ export interface NoteKeypair {
5
+ /** Raw 32-byte scalar, strictly below the BN254 Fr modulus. */
6
+ noteSecretKey: Uint8Array;
7
+ /** Hex form, matching how the relayer has always returned this key. */
8
+ noteSecretKeyHex: string;
9
+ /** Poseidon(noteSecretKey) — the note's committed owner. */
10
+ notePublicKey: bigint;
11
+ /** Decimal string form, as the relayer and note scanner expect it. */
12
+ notePublicKeyString: string;
13
+ }
14
+ /**
15
+ * Derive the note spending key from a spending-key root.
16
+ *
17
+ * @param spendingKey 32 bytes from `deriveSpendingKeyFromSignature` or a sibling
18
+ */
19
+ export declare function deriveNoteKeyFromSpendingKey(spendingKey: Uint8Array): Uint8Array;
20
+ /**
21
+ * Derive the note spending key and its Poseidon public key.
22
+ *
23
+ * Call `initPoseidon()` once before this, as with `derivePositionKeys`.
24
+ */
25
+ export declare function deriveNoteKeypairFromSpendingKey(spendingKey: Uint8Array): NoteKeypair;
26
+ /**
27
+ * One-shot derivation for a dapp holding only a wallet-standard `signMessage`.
28
+ *
29
+ * Prompts a single signature, then returns spend authority for the account.
30
+ * The signature is secret material: keep it in memory, never persist it, and
31
+ * never expose the result on a global.
32
+ */
33
+ export declare function deriveNoteKeypairWithSigner(signMessage: (message: Uint8Array) => Promise<Uint8Array>, index?: number): Promise<NoteKeypair>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Derivation of the Veilo NOTE SPENDING KEY from a spending-key root.
3
+ *
4
+ * ⚠ BYTE-FOR-BYTE PORT — must agree with
5
+ * veilo-browser-extension/extension-ui/src/lib/veiloNoteKey.ts
6
+ * wallet-app/src/lib/veiloNoteKey.ts
7
+ * An account created in one client must be spendable from the others, so the
8
+ * label, the HMAC construction and the rejection rule are all load-bearing.
9
+ * `tests/identity-keys.test.ts` re-implements the client algorithm
10
+ * independently and asserts equality, plus pins a known-answer vector.
11
+ *
12
+ * WHAT THIS KEY IS
13
+ * ----------------
14
+ * `zk-circuits/keypair.circom` defines publicKey = Poseidon(privateKey), and
15
+ * `transaction.circom` constrains Poseidon(privateKey) === the note's committed
16
+ * owner before it will emit a proof, then derives the nullifier from the same
17
+ * key. This one scalar is both proof of ownership and spend authority; there is
18
+ * no second factor anywhere in the circuit. Treat it exactly as you would a
19
+ * wallet secret key.
20
+ *
21
+ * Derivation:
22
+ * spendingKey = deriveSpendingKeyFromSignature(sig) // ./spending.js
23
+ * veiloPrivateKey = rejectionSample(hmac_sha256(spendingKey, "veilo_note_key_v1|N"))
24
+ * veiloPublicKey = Poseidon(veiloPrivateKey)
25
+ *
26
+ * WHY REJECTION SAMPLING AND NOT `mod Fr`
27
+ * ---------------------------------------
28
+ * Reducing a 256-bit hash modulo Fr biases the low end of the range. Note that
29
+ * `derivePositionKeys` uses modular reduction; this key follows the stricter
30
+ * convention because it is a SPENDING key, matching how real note secrets are
31
+ * generated. About 0.3% of draws are retried.
32
+ *
33
+ * ⚠ THE LABEL IS PERMANENT. Once one note exists under "veilo_note_key_v1",
34
+ * changing the label or the sampling method makes that note unspendable
35
+ * forever. Version it, never edit it.
36
+ *
37
+ * ⚠ APPLIES TO `derived-v3` ACCOUNTS ONLY. Accounts whose relayer record has
38
+ * `veiloKeySource` absent or "legacy" have a SERVER-GENERATED note key that no
39
+ * derivation can reproduce — those clients must decrypt the key the relayer
40
+ * serves them. Check the account's key source before assuming this function
41
+ * yields a usable key.
42
+ */
43
+ import { hmac } from "@noble/hashes/hmac";
44
+ import { sha256 } from "@noble/hashes/sha256";
45
+ import { BN254_FR_MODULUS, bytesToBigIntBE, poseidon1 } from "../poseidon.js";
46
+ import { assertBytes, bytesToHex, utf8 } from "./encoding.js";
47
+ import { deriveSpendingKeyWithSigner } from "./spending.js";
48
+ /** Domain-separation label. Permanent — see the header. */
49
+ export const VEILO_NOTE_KEY_LABEL = "veilo_note_key_v1";
50
+ /** Practically unreachable: 256 consecutive rejections is ~1e-640. */
51
+ const MAX_REJECTION_ROUNDS = 256;
52
+ /**
53
+ * Derive the note spending key from a spending-key root.
54
+ *
55
+ * @param spendingKey 32 bytes from `deriveSpendingKeyFromSignature` or a sibling
56
+ */
57
+ export function deriveNoteKeyFromSpendingKey(spendingKey) {
58
+ assertBytes(spendingKey, 32, "spending key");
59
+ for (let counter = 0; counter < MAX_REJECTION_ROUNDS; counter++) {
60
+ const candidate = hmac(sha256, spendingKey, utf8(`${VEILO_NOTE_KEY_LABEL}|${counter}`));
61
+ if (bytesToBigIntBE(candidate) < BN254_FR_MODULUS)
62
+ return candidate;
63
+ }
64
+ // Reaching here means HMAC-SHA256 is broken, not that we were unlucky.
65
+ throw new Error(`Failed to derive a field element for "${VEILO_NOTE_KEY_LABEL}"`);
66
+ }
67
+ /**
68
+ * Derive the note spending key and its Poseidon public key.
69
+ *
70
+ * Call `initPoseidon()` once before this, as with `derivePositionKeys`.
71
+ */
72
+ export function deriveNoteKeypairFromSpendingKey(spendingKey) {
73
+ const noteSecretKey = deriveNoteKeyFromSpendingKey(spendingKey);
74
+ const notePublicKey = poseidon1(bytesToBigIntBE(noteSecretKey));
75
+ return {
76
+ noteSecretKey,
77
+ noteSecretKeyHex: bytesToHex(noteSecretKey),
78
+ notePublicKey,
79
+ notePublicKeyString: notePublicKey.toString(),
80
+ };
81
+ }
82
+ /**
83
+ * One-shot derivation for a dapp holding only a wallet-standard `signMessage`.
84
+ *
85
+ * Prompts a single signature, then returns spend authority for the account.
86
+ * The signature is secret material: keep it in memory, never persist it, and
87
+ * never expose the result on a global.
88
+ */
89
+ export async function deriveNoteKeypairWithSigner(signMessage, index = 0) {
90
+ return deriveNoteKeypairFromSpendingKey(await deriveSpendingKeyWithSigner(signMessage, index));
91
+ }
@@ -0,0 +1,18 @@
1
+ import { Keypair } from "@solana/web3.js";
2
+ export declare const VEILO_SPENDING_KEY_DOMAIN = "veilo_spending_key_v1";
3
+ /** Exact message signed by Veilo clients to derive a spending-key root. */
4
+ export declare function getSpendingKeyMessage(index?: number): Uint8Array;
5
+ /** Convert the deterministic 64-byte wallet signature into a spending key. */
6
+ export declare function deriveSpendingKeyFromSignature(signature: Uint8Array): Uint8Array;
7
+ /**
8
+ * Wallet-standard-friendly spending-key derivation.
9
+ * The callback owns signing; the SDK never receives the wallet secret key.
10
+ */
11
+ export declare function deriveSpendingKeyWithSigner(signMessage: (message: Uint8Array) => Promise<Uint8Array>, index?: number): Promise<Uint8Array>;
12
+ /**
13
+ * Keypair convenience matching the wallet-app and extension implementation.
14
+ * Keep the keypair on the user's device and never send it to a service.
15
+ */
16
+ export declare function deriveSpendingKeyFromKeypair(keypair: Keypair, index?: number): Uint8Array;
17
+ /** Serialize an ephemeral claimant secret for an encrypted relayer request. */
18
+ export declare function encodeClaimantSecretKey(keypairOrSecret: Keypair | Uint8Array): string;
@@ -0,0 +1,35 @@
1
+ import { sha256 } from "@noble/hashes/sha256";
2
+ import nacl from "tweetnacl";
3
+ import { assertBytes, concatBytes, encodeBase58, uint32Le, utf8, } from "./encoding.js";
4
+ export const VEILO_SPENDING_KEY_DOMAIN = "veilo_spending_key_v1";
5
+ /** Exact message signed by Veilo clients to derive a spending-key root. */
6
+ export function getSpendingKeyMessage(index = 0) {
7
+ return concatBytes(utf8(VEILO_SPENDING_KEY_DOMAIN), uint32Le(index, "spending-key index"));
8
+ }
9
+ /** Convert the deterministic 64-byte wallet signature into a spending key. */
10
+ export function deriveSpendingKeyFromSignature(signature) {
11
+ assertBytes(signature, 64, "spending-key signature");
12
+ return sha256(signature);
13
+ }
14
+ /**
15
+ * Wallet-standard-friendly spending-key derivation.
16
+ * The callback owns signing; the SDK never receives the wallet secret key.
17
+ */
18
+ export async function deriveSpendingKeyWithSigner(signMessage, index = 0) {
19
+ return deriveSpendingKeyFromSignature(await signMessage(getSpendingKeyMessage(index)));
20
+ }
21
+ /**
22
+ * Keypair convenience matching the wallet-app and extension implementation.
23
+ * Keep the keypair on the user's device and never send it to a service.
24
+ */
25
+ export function deriveSpendingKeyFromKeypair(keypair, index = 0) {
26
+ return deriveSpendingKeyFromSignature(nacl.sign.detached(getSpendingKeyMessage(index), keypair.secretKey));
27
+ }
28
+ /** Serialize an ephemeral claimant secret for an encrypted relayer request. */
29
+ export function encodeClaimantSecretKey(keypairOrSecret) {
30
+ const secret = keypairOrSecret instanceof Uint8Array
31
+ ? keypairOrSecret
32
+ : keypairOrSecret.secretKey;
33
+ assertBytes(secret, 64, "claimant secret key");
34
+ return encodeBase58(secret);
35
+ }
@@ -0,0 +1,38 @@
1
+ import type { CompactNoteCipher } from "../compactNote.js";
2
+ /** HKDF salt. Provisional — see the header. */
3
+ export declare const VEILO_VIEW_KEY_SALT = "veilo_view_v1";
4
+ /** HKDF info. Provisional — see the header. */
5
+ export declare const VEILO_VIEW_KEY_INFO = "x25519";
6
+ /** An X25519 keypair used only to decrypt notes. */
7
+ export interface ViewKeypair {
8
+ /** 32-byte clamped X25519 scalar. Secret: decrypts every note sent to it. */
9
+ viewSecretKey: Uint8Array;
10
+ /** 32-byte X25519 public key — the value senders encrypt to. */
11
+ viewPublicKey: Uint8Array;
12
+ }
13
+ /** Derive the view keypair from a spending-key root. */
14
+ export declare function deriveViewKeypairFromSpendingKey(spendingKey: Uint8Array): ViewKeypair;
15
+ /**
16
+ * One-shot derivation for a dapp holding only a wallet-standard `signMessage`.
17
+ *
18
+ * Uses the same signature as `deriveNoteKeypairWithSigner`, so a client that
19
+ * wants both prompts once and derives the spending key itself rather than
20
+ * calling both helpers.
21
+ */
22
+ export declare function deriveViewKeypairWithSigner(signMessage: (message: Uint8Array) => Promise<Uint8Array>, index?: number): Promise<ViewKeypair>;
23
+ /**
24
+ * Scan one candidate cipher with a view key.
25
+ *
26
+ * Returns `null` both for a view-tag miss and for a blob that was not encrypted
27
+ * to this key — the expected outcome for nearly every candidate. The shared
28
+ * secret is computed once and reused for the tag check and the open, which is
29
+ * the cheapest correct order for a scan loop: the tag rejects ~255/256
30
+ * candidates for one scalar multiplication and one hash.
31
+ *
32
+ * ⚠ Only decrypts notes encrypted TO `viewPublicKey`. Notes sealed to a wallet
33
+ * Ed25519 key — every note written before the v2 format — will not open here.
34
+ */
35
+ export declare function openCompactNoteWithViewKey(viewSecretKey: Uint8Array, cipher: CompactNoteCipher): {
36
+ blinding: Uint8Array;
37
+ amount: bigint;
38
+ } | null;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Derivation of a VIEW-ONLY note decryption key from a spending-key root.
3
+ *
4
+ * ⚠ PROVISIONAL — NOT YET CONSUMED BY ANY NOTE FORMAT.
5
+ * Today's compact note ciphers are ECDH'd to the recipient's *wallet* Ed25519
6
+ * key (see `createCompactNoteCipher`), so nothing on-chain or in the relayer is
7
+ * encrypted to the key this module derives. It exists so the v2 note format —
8
+ * where senders encrypt to a published `viewPublicKey` instead of the wallet
9
+ * key — can be built and tested against a fixed derivation. The labels below
10
+ * become permanent the moment the first note is written under them; until then
11
+ * they are safe to change.
12
+ *
13
+ * WHY THIS EXISTS
14
+ * ---------------
15
+ * Note decryption currently requires the wallet Ed25519 secret, which is the
16
+ * one thing a dapp cannot have. Spend authority, by contrast, is already
17
+ * signature-derived (`./noteKey.js`). So a dapp can reconstruct the ability to
18
+ * spend but not the ability to *read*, and since a spend witness needs each
19
+ * note's amount and blinding, reading gates spending. Publishing a
20
+ * signature-derived `viewPublicKey` and encrypting to it closes that gap.
21
+ *
22
+ * Splitting viewing from spending is the point: `viewSecretKey` decrypts notes
23
+ * and nothing more. It cannot produce a nullifier, so it cannot spend, and it
24
+ * cannot determine on its own whether a note is already spent — a view-only
25
+ * client relies on the relayer's `spent` flag for that.
26
+ *
27
+ * Derivation:
28
+ * viewSecretKey = clamp(HKDF-SHA256(ikm=spendingKey, salt="veilo_view_v1",
29
+ * info="x25519", len=32))
30
+ * viewPublicKey = X25519_base(viewSecretKey)
31
+ *
32
+ * Both salt and info are given explicitly rather than left empty, so a sibling
33
+ * implementation never has to guess how its HKDF library treats an absent salt.
34
+ */
35
+ import { hkdf } from "@noble/hashes/hkdf";
36
+ import { sha256 } from "@noble/hashes/sha256";
37
+ import nacl from "tweetnacl";
38
+ import { decryptCompactNoteCipher } from "../compactNote.js";
39
+ import { ed25519PublicKeyToX25519 } from "../notes/mailbox.js";
40
+ import { assertBytes, utf8 } from "./encoding.js";
41
+ import { deriveSpendingKeyWithSigner } from "./spending.js";
42
+ /** HKDF salt. Provisional — see the header. */
43
+ export const VEILO_VIEW_KEY_SALT = "veilo_view_v1";
44
+ /** HKDF info. Provisional — see the header. */
45
+ export const VEILO_VIEW_KEY_INFO = "x25519";
46
+ /**
47
+ * Apply RFC 7748 clamping so the stored scalar is canonical.
48
+ *
49
+ * TweetNaCl clamps internally on every scalar multiplication, so this changes
50
+ * no result; it means the bytes we hand out are the bytes actually used, which
51
+ * keeps a sibling implementation from disagreeing about what was stored.
52
+ */
53
+ function clampX25519(scalar) {
54
+ const clamped = new Uint8Array(scalar);
55
+ clamped[0] &= 248;
56
+ clamped[31] &= 127;
57
+ clamped[31] |= 64;
58
+ return clamped;
59
+ }
60
+ /** Derive the view keypair from a spending-key root. */
61
+ export function deriveViewKeypairFromSpendingKey(spendingKey) {
62
+ assertBytes(spendingKey, 32, "spending key");
63
+ const viewSecretKey = clampX25519(hkdf(sha256, spendingKey, utf8(VEILO_VIEW_KEY_SALT), utf8(VEILO_VIEW_KEY_INFO), 32));
64
+ return { viewSecretKey, viewPublicKey: nacl.scalarMult.base(viewSecretKey) };
65
+ }
66
+ /**
67
+ * One-shot derivation for a dapp holding only a wallet-standard `signMessage`.
68
+ *
69
+ * Uses the same signature as `deriveNoteKeypairWithSigner`, so a client that
70
+ * wants both prompts once and derives the spending key itself rather than
71
+ * calling both helpers.
72
+ */
73
+ export async function deriveViewKeypairWithSigner(signMessage, index = 0) {
74
+ return deriveViewKeypairFromSpendingKey(await deriveSpendingKeyWithSigner(signMessage, index));
75
+ }
76
+ /**
77
+ * Scan one candidate cipher with a view key.
78
+ *
79
+ * Returns `null` both for a view-tag miss and for a blob that was not encrypted
80
+ * to this key — the expected outcome for nearly every candidate. The shared
81
+ * secret is computed once and reused for the tag check and the open, which is
82
+ * the cheapest correct order for a scan loop: the tag rejects ~255/256
83
+ * candidates for one scalar multiplication and one hash.
84
+ *
85
+ * ⚠ Only decrypts notes encrypted TO `viewPublicKey`. Notes sealed to a wallet
86
+ * Ed25519 key — every note written before the v2 format — will not open here.
87
+ */
88
+ export function openCompactNoteWithViewKey(viewSecretKey, cipher) {
89
+ assertBytes(viewSecretKey, 32, "view secret key");
90
+ assertBytes(cipher.ephemeralPublicKey, 32, "ephemeral public key");
91
+ const sharedSecret = nacl.scalarMult(viewSecretKey, ed25519PublicKeyToX25519(cipher.ephemeralPublicKey));
92
+ if (nacl.hash(sharedSecret)[0] !== cipher.viewTag)
93
+ return null;
94
+ // walletSecretKey is unused once `sharedSecret` is supplied.
95
+ return decryptCompactNoteCipher(viewSecretKey, cipher.ephemeralPublicKey, cipher.compactBlob, { sharedSecret });
96
+ }
@@ -0,0 +1,18 @@
1
+ import type { Idl } from "@coral-xyz/anchor";
2
+ /** Protocol generations used to decode Veilo's historical on-chain data. */
3
+ export type PrivacyPoolIdlVersion = "current" | "legacy" | "legacy2";
4
+ export type VersionedPrivacyPoolIdl = {
5
+ version: PrivacyPoolIdlVersion;
6
+ idl: Idl;
7
+ };
8
+ /** Current IDL used for building new instructions. */
9
+ export declare const PRIVACY_POOL_CURRENT_IDL: Idl;
10
+ /** Original event shape without compact note recovery fields. */
11
+ export declare const PRIVACY_POOL_LEGACY_IDL: Idl;
12
+ /** Intermediate event shape with compact cipher fields but no view tag. */
13
+ export declare const PRIVACY_POOL_LEGACY2_IDL: Idl;
14
+ /**
15
+ * IDLs in the same newest-to-oldest fallback order used by the relayer indexer.
16
+ * All three target the same deployed program address.
17
+ */
18
+ export declare const PRIVACY_POOL_IDLS: readonly VersionedPrivacyPoolIdl[];
@@ -0,0 +1,18 @@
1
+ import currentIdl from "./privacy_pool.js";
2
+ import legacyIdl from "./privacy_pool_legacy.js";
3
+ import legacy2Idl from "./privacy_pool_legacy2.js";
4
+ /** Current IDL used for building new instructions. */
5
+ export const PRIVACY_POOL_CURRENT_IDL = currentIdl;
6
+ /** Original event shape without compact note recovery fields. */
7
+ export const PRIVACY_POOL_LEGACY_IDL = legacyIdl;
8
+ /** Intermediate event shape with compact cipher fields but no view tag. */
9
+ export const PRIVACY_POOL_LEGACY2_IDL = legacy2Idl;
10
+ /**
11
+ * IDLs in the same newest-to-oldest fallback order used by the relayer indexer.
12
+ * All three target the same deployed program address.
13
+ */
14
+ export const PRIVACY_POOL_IDLS = [
15
+ { version: "current", idl: PRIVACY_POOL_CURRENT_IDL },
16
+ { version: "legacy", idl: PRIVACY_POOL_LEGACY_IDL },
17
+ { version: "legacy2", idl: PRIVACY_POOL_LEGACY2_IDL },
18
+ ];