@sip-protocol/sdk 0.7.3 → 0.8.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 (263) hide show
  1. package/README.md +267 -0
  2. package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
  3. package/dist/browser.d.mts +10 -4
  4. package/dist/browser.d.ts +10 -4
  5. package/dist/browser.js +47556 -19603
  6. package/dist/browser.mjs +628 -48
  7. package/dist/chunk-4GRJ5MAW.mjs +152 -0
  8. package/dist/chunk-5D7A3L3W.mjs +717 -0
  9. package/dist/chunk-64AYA5F5.mjs +7834 -0
  10. package/dist/chunk-GMDGB22A.mjs +379 -0
  11. package/dist/chunk-I534WKN7.mjs +328 -0
  12. package/dist/chunk-IBZVA5Y7.mjs +1003 -0
  13. package/dist/chunk-PRRZAWJE.mjs +223 -0
  14. package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
  15. package/dist/{chunk-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
  16. package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
  17. package/dist/constants-LHAAUC2T.mjs +51 -0
  18. package/dist/dist-2OGQ7FED.mjs +3957 -0
  19. package/dist/dist-IFHPYLDX.mjs +254 -0
  20. package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
  21. package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
  22. package/dist/{index-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
  23. package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
  24. package/dist/index.d.mts +9 -3
  25. package/dist/index.d.ts +9 -3
  26. package/dist/index.js +48396 -19623
  27. package/dist/index.mjs +537 -19
  28. package/dist/interface-Bf7w1PLW.d.mts +679 -0
  29. package/dist/interface-Bf7w1PLW.d.ts +679 -0
  30. package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
  31. package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
  32. package/dist/proofs/halo2.d.mts +151 -0
  33. package/dist/proofs/halo2.d.ts +151 -0
  34. package/dist/proofs/halo2.js +350 -0
  35. package/dist/proofs/halo2.mjs +11 -0
  36. package/dist/proofs/kimchi.d.mts +160 -0
  37. package/dist/proofs/kimchi.d.ts +160 -0
  38. package/dist/proofs/kimchi.js +431 -0
  39. package/dist/proofs/kimchi.mjs +13 -0
  40. package/dist/proofs/noir.d.mts +1 -1
  41. package/dist/proofs/noir.d.ts +1 -1
  42. package/dist/proofs/noir.js +74 -18
  43. package/dist/proofs/noir.mjs +84 -24
  44. package/dist/solana-U3MEGU7W.mjs +280 -0
  45. package/dist/validity_proof-3POXLPNY.mjs +21 -0
  46. package/package.json +44 -11
  47. package/src/adapters/index.ts +41 -0
  48. package/src/adapters/jupiter.ts +571 -0
  49. package/src/adapters/near-intents.ts +135 -0
  50. package/src/advisor/advisor.ts +653 -0
  51. package/src/advisor/index.ts +54 -0
  52. package/src/advisor/tools.ts +303 -0
  53. package/src/advisor/types.ts +164 -0
  54. package/src/chains/ethereum/announcement.ts +536 -0
  55. package/src/chains/ethereum/bnb-optimizations.ts +474 -0
  56. package/src/chains/ethereum/commitment.ts +522 -0
  57. package/src/chains/ethereum/constants.ts +462 -0
  58. package/src/chains/ethereum/deployment.ts +596 -0
  59. package/src/chains/ethereum/gas-estimation.ts +538 -0
  60. package/src/chains/ethereum/index.ts +268 -0
  61. package/src/chains/ethereum/optimizations.ts +614 -0
  62. package/src/chains/ethereum/privacy-adapter.ts +855 -0
  63. package/src/chains/ethereum/registry.ts +584 -0
  64. package/src/chains/ethereum/rpc.ts +905 -0
  65. package/src/chains/ethereum/stealth.ts +491 -0
  66. package/src/chains/ethereum/token.ts +790 -0
  67. package/src/chains/ethereum/transfer.ts +637 -0
  68. package/src/chains/ethereum/types.ts +456 -0
  69. package/src/chains/ethereum/viewing-key.ts +455 -0
  70. package/src/chains/near/commitment.ts +608 -0
  71. package/src/chains/near/constants.ts +284 -0
  72. package/src/chains/near/function-call.ts +871 -0
  73. package/src/chains/near/history.ts +654 -0
  74. package/src/chains/near/implicit-account.ts +840 -0
  75. package/src/chains/near/index.ts +393 -0
  76. package/src/chains/near/native-transfer.ts +658 -0
  77. package/src/chains/near/nep141.ts +775 -0
  78. package/src/chains/near/privacy-adapter.ts +889 -0
  79. package/src/chains/near/resolver.ts +971 -0
  80. package/src/chains/near/rpc.ts +1016 -0
  81. package/src/chains/near/stealth.ts +419 -0
  82. package/src/chains/near/types.ts +317 -0
  83. package/src/chains/near/viewing-key.ts +876 -0
  84. package/src/chains/solana/anchor-transfer.ts +386 -0
  85. package/src/chains/solana/commitment.ts +577 -0
  86. package/src/chains/solana/constants.ts +126 -12
  87. package/src/chains/solana/ephemeral-keys.ts +543 -0
  88. package/src/chains/solana/index.ts +252 -1
  89. package/src/chains/solana/key-derivation.ts +418 -0
  90. package/src/chains/solana/kit-compat.ts +334 -0
  91. package/src/chains/solana/optimizations.ts +560 -0
  92. package/src/chains/solana/privacy-adapter.ts +605 -0
  93. package/src/chains/solana/providers/generic.ts +47 -6
  94. package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
  95. package/src/chains/solana/providers/helius-enhanced.ts +623 -0
  96. package/src/chains/solana/providers/helius.ts +186 -33
  97. package/src/chains/solana/providers/index.ts +31 -0
  98. package/src/chains/solana/providers/interface.ts +61 -18
  99. package/src/chains/solana/providers/quicknode.ts +409 -0
  100. package/src/chains/solana/providers/triton.ts +426 -0
  101. package/src/chains/solana/providers/webhook.ts +338 -67
  102. package/src/chains/solana/rpc-client.ts +1150 -0
  103. package/src/chains/solana/scan.ts +83 -66
  104. package/src/chains/solana/sol-transfer.ts +732 -0
  105. package/src/chains/solana/spl-transfer.ts +886 -0
  106. package/src/chains/solana/stealth-scanner.ts +703 -0
  107. package/src/chains/solana/sunspot-verifier.ts +453 -0
  108. package/src/chains/solana/transaction-builder.ts +755 -0
  109. package/src/chains/solana/transfer.ts +74 -5
  110. package/src/chains/solana/types.ts +57 -6
  111. package/src/chains/solana/utils.ts +110 -0
  112. package/src/chains/solana/viewing-key.ts +807 -0
  113. package/src/compliance/fireblocks.ts +921 -0
  114. package/src/compliance/index.ts +23 -0
  115. package/src/compliance/range-sas.ts +398 -33
  116. package/src/config/endpoints.ts +100 -0
  117. package/src/crypto.ts +11 -8
  118. package/src/errors.ts +82 -0
  119. package/src/evm/erc4337-relayer.ts +830 -0
  120. package/src/evm/index.ts +47 -0
  121. package/src/fees/calculator.ts +396 -0
  122. package/src/fees/index.ts +87 -0
  123. package/src/fees/near-contract.ts +429 -0
  124. package/src/fees/types.ts +268 -0
  125. package/src/index.ts +686 -1
  126. package/src/intent.ts +6 -3
  127. package/src/logger.ts +324 -0
  128. package/src/network/index.ts +80 -0
  129. package/src/network/proxy.ts +691 -0
  130. package/src/optimizations/index.ts +541 -0
  131. package/src/oracle/types.ts +1 -0
  132. package/src/privacy-backends/arcium-types.ts +727 -0
  133. package/src/privacy-backends/arcium.ts +719 -0
  134. package/src/privacy-backends/combined-privacy.ts +866 -0
  135. package/src/privacy-backends/cspl-token.ts +595 -0
  136. package/src/privacy-backends/cspl-types.ts +512 -0
  137. package/src/privacy-backends/cspl.ts +907 -0
  138. package/src/privacy-backends/health.ts +488 -0
  139. package/src/privacy-backends/inco-types.ts +323 -0
  140. package/src/privacy-backends/inco.ts +616 -0
  141. package/src/privacy-backends/index.ts +254 -4
  142. package/src/privacy-backends/interface.ts +649 -6
  143. package/src/privacy-backends/lru-cache.ts +343 -0
  144. package/src/privacy-backends/magicblock.ts +458 -0
  145. package/src/privacy-backends/mock.ts +258 -0
  146. package/src/privacy-backends/privacycash.ts +13 -17
  147. package/src/privacy-backends/private-swap.ts +570 -0
  148. package/src/privacy-backends/rate-limiter.ts +683 -0
  149. package/src/privacy-backends/registry.ts +414 -2
  150. package/src/privacy-backends/router.ts +283 -3
  151. package/src/privacy-backends/shadowwire.ts +449 -0
  152. package/src/privacy-backends/sip-native.ts +3 -0
  153. package/src/privacy-logger.ts +191 -0
  154. package/src/production-safety.ts +373 -0
  155. package/src/proofs/aggregator.ts +1029 -0
  156. package/src/proofs/browser-composer.ts +1150 -0
  157. package/src/proofs/browser.ts +113 -25
  158. package/src/proofs/cache/index.ts +127 -0
  159. package/src/proofs/cache/interface.ts +545 -0
  160. package/src/proofs/cache/key-generator.ts +188 -0
  161. package/src/proofs/cache/lru-cache.ts +481 -0
  162. package/src/proofs/cache/multi-tier-cache.ts +575 -0
  163. package/src/proofs/cache/persistent-cache.ts +788 -0
  164. package/src/proofs/compliance-proof.ts +872 -0
  165. package/src/proofs/composer/base.ts +923 -0
  166. package/src/proofs/composer/index.ts +25 -0
  167. package/src/proofs/composer/interface.ts +518 -0
  168. package/src/proofs/composer/types.ts +383 -0
  169. package/src/proofs/converters/halo2.ts +452 -0
  170. package/src/proofs/converters/index.ts +208 -0
  171. package/src/proofs/converters/interface.ts +363 -0
  172. package/src/proofs/converters/kimchi.ts +462 -0
  173. package/src/proofs/converters/noir.ts +451 -0
  174. package/src/proofs/fallback.ts +888 -0
  175. package/src/proofs/halo2.ts +42 -0
  176. package/src/proofs/index.ts +471 -0
  177. package/src/proofs/interface.ts +13 -0
  178. package/src/proofs/kimchi.ts +42 -0
  179. package/src/proofs/lazy.ts +1004 -0
  180. package/src/proofs/mock.ts +25 -1
  181. package/src/proofs/noir.ts +110 -29
  182. package/src/proofs/orchestrator.ts +960 -0
  183. package/src/proofs/parallel/concurrency.ts +297 -0
  184. package/src/proofs/parallel/dependency-graph.ts +602 -0
  185. package/src/proofs/parallel/executor.ts +420 -0
  186. package/src/proofs/parallel/index.ts +131 -0
  187. package/src/proofs/parallel/interface.ts +685 -0
  188. package/src/proofs/parallel/worker-pool.ts +644 -0
  189. package/src/proofs/providers/halo2.ts +560 -0
  190. package/src/proofs/providers/index.ts +34 -0
  191. package/src/proofs/providers/kimchi.ts +641 -0
  192. package/src/proofs/validator.ts +881 -0
  193. package/src/proofs/verifier.ts +867 -0
  194. package/src/quantum/index.ts +112 -0
  195. package/src/quantum/winternitz-vault.ts +639 -0
  196. package/src/quantum/wots.ts +611 -0
  197. package/src/settlement/backends/direct-chain.ts +1 -0
  198. package/src/settlement/index.ts +9 -0
  199. package/src/settlement/router.ts +732 -46
  200. package/src/solana/index.ts +72 -0
  201. package/src/solana/jito-relayer.ts +687 -0
  202. package/src/solana/noir-verifier-types.ts +430 -0
  203. package/src/solana/noir-verifier.ts +816 -0
  204. package/src/stealth/address-derivation.ts +193 -0
  205. package/src/stealth/ed25519.ts +431 -0
  206. package/src/stealth/index.ts +233 -0
  207. package/src/stealth/meta-address.ts +221 -0
  208. package/src/stealth/secp256k1.ts +368 -0
  209. package/src/stealth/utils.ts +194 -0
  210. package/src/stealth.ts +50 -1504
  211. package/src/sync/index.ts +106 -0
  212. package/src/sync/manager.ts +504 -0
  213. package/src/sync/mock-provider.ts +318 -0
  214. package/src/sync/oblivious.ts +625 -0
  215. package/src/tokens/index.ts +15 -0
  216. package/src/tokens/registry.ts +301 -0
  217. package/src/utils/deprecation.ts +94 -0
  218. package/src/utils/index.ts +9 -0
  219. package/src/wallet/ethereum/index.ts +68 -0
  220. package/src/wallet/ethereum/metamask-privacy.ts +420 -0
  221. package/src/wallet/ethereum/multi-wallet.ts +646 -0
  222. package/src/wallet/ethereum/privacy-adapter.ts +700 -0
  223. package/src/wallet/ethereum/types.ts +3 -1
  224. package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
  225. package/src/wallet/hardware/index.ts +10 -0
  226. package/src/wallet/hardware/ledger-privacy.ts +414 -0
  227. package/src/wallet/index.ts +71 -0
  228. package/src/wallet/near/adapter.ts +626 -0
  229. package/src/wallet/near/index.ts +86 -0
  230. package/src/wallet/near/meteor-wallet.ts +1153 -0
  231. package/src/wallet/near/my-near-wallet.ts +790 -0
  232. package/src/wallet/near/wallet-selector.ts +702 -0
  233. package/src/wallet/solana/adapter.ts +6 -4
  234. package/src/wallet/solana/index.ts +13 -0
  235. package/src/wallet/solana/privacy-adapter.ts +567 -0
  236. package/src/wallet/sui/types.ts +6 -4
  237. package/src/zcash/rpc-client.ts +13 -6
  238. package/dist/chunk-2XIVXWHA.mjs +0 -1930
  239. package/dist/chunk-3INS3PR5.mjs +0 -884
  240. package/dist/chunk-3OVABDRH.mjs +0 -17096
  241. package/dist/chunk-7RFRWDCW.mjs +0 -1504
  242. package/dist/chunk-DLDWZFYC.mjs +0 -1495
  243. package/dist/chunk-E6SZWREQ.mjs +0 -57
  244. package/dist/chunk-F6F73W35.mjs +0 -16166
  245. package/dist/chunk-G33LB27A.mjs +0 -16166
  246. package/dist/chunk-HGU6HZRC.mjs +0 -231
  247. package/dist/chunk-L2K34JCU.mjs +0 -1496
  248. package/dist/chunk-OFDBEIEK.mjs +0 -16166
  249. package/dist/chunk-SF7YSLF5.mjs +0 -1515
  250. package/dist/chunk-SN4ZDTVW.mjs +0 -16166
  251. package/dist/chunk-WWUSGOXE.mjs +0 -17129
  252. package/dist/constants-VOI7BSLK.mjs +0 -27
  253. package/dist/index-B71aXVzk.d.ts +0 -13264
  254. package/dist/index-BYZbDjal.d.ts +0 -11390
  255. package/dist/index-CHB3KuOB.d.mts +0 -11859
  256. package/dist/index-CzWPI6Le.d.ts +0 -11859
  257. package/dist/index-pOIIuwfV.d.mts +0 -13264
  258. package/dist/index-xbWjohNq.d.mts +0 -11390
  259. package/dist/solana-4O4K45VU.mjs +0 -46
  260. package/dist/solana-5EMCTPTS.mjs +0 -46
  261. package/dist/solana-NDABAZ6P.mjs +0 -56
  262. package/dist/solana-Q4NAVBTS.mjs +0 -46
  263. package/dist/solana-ZYO63LY5.mjs +0 -46
@@ -0,0 +1,625 @@
1
+ /**
2
+ * Oblivious Sync Service Interface
3
+ *
4
+ * Implements oblivious synchronization where sync services learn NOTHING
5
+ * about user transactions. Inspired by Project Tachyon.
6
+ *
7
+ * @see https://seanbowe.com/blog/tachyon-scaling-zcash-oblivious-synchronization/
8
+ * @see https://github.com/sip-protocol/sip-protocol/issues/433
9
+ *
10
+ * ## How It Works
11
+ *
12
+ * Traditional sync services leak information:
13
+ * - Which nullifiers you're checking (reveals spend timing)
14
+ * - Block access patterns (correlates with activity)
15
+ *
16
+ * Oblivious sync prevents this by:
17
+ * 1. Using sync randomness in nullifier derivation
18
+ * 2. Encrypting queries so service learns nothing
19
+ * 3. Supporting time-windowed viewing key disclosure
20
+ *
21
+ * @module sync/oblivious
22
+ */
23
+
24
+ import type { HexString, Hash } from '@sip-protocol/types'
25
+ import { sha256 } from '@noble/hashes/sha2'
26
+ import { hmac } from '@noble/hashes/hmac'
27
+ import { bytesToHex, hexToBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'
28
+ import { secureWipe } from '../secure-memory'
29
+
30
+ // ─── Types ────────────────────────────────────────────────────────────────────
31
+
32
+ /**
33
+ * Chain identifier for multi-chain sync
34
+ */
35
+ export type ChainId = string
36
+
37
+ /**
38
+ * Block range for synchronization
39
+ */
40
+ export interface BlockRange {
41
+ /** Starting block (inclusive) */
42
+ startBlock: bigint
43
+ /** Ending block (inclusive) */
44
+ endBlock: bigint
45
+ /** Chain ID */
46
+ chainId: ChainId
47
+ }
48
+
49
+ /**
50
+ * Encrypted note that sync service returns without being able to decrypt
51
+ */
52
+ export interface EncryptedNote {
53
+ /** Note commitment (public) */
54
+ commitment: HexString
55
+ /** Encrypted note data (only owner can decrypt) */
56
+ encryptedData: HexString
57
+ /** Block number where note was created */
58
+ blockNumber: bigint
59
+ /** Transaction hash */
60
+ txHash: HexString
61
+ /** Chain ID */
62
+ chainId: ChainId
63
+ }
64
+
65
+ /**
66
+ * Merkle proof for note inclusion
67
+ */
68
+ export interface MerkleProof {
69
+ /** Leaf being proven */
70
+ leaf: HexString
71
+ /** Sibling hashes along path */
72
+ siblings: HexString[]
73
+ /** Index of leaf in tree */
74
+ index: bigint
75
+ /** Root of the tree */
76
+ root: HexString
77
+ }
78
+
79
+ /**
80
+ * Sync randomness used in oblivious nullifier derivation
81
+ *
82
+ * This is the key innovation from Tachyon: by including sync randomness
83
+ * in nullifier computation, the sync service cannot correlate nullifiers
84
+ * to specific notes.
85
+ */
86
+ export interface SyncRandomness {
87
+ /** Random bytes (32 bytes) */
88
+ value: Uint8Array
89
+ /** Epoch/period this randomness is valid for */
90
+ epoch: number
91
+ /** Expiration timestamp */
92
+ expiresAt: number
93
+ }
94
+
95
+ /**
96
+ * Oblivious nullifier - cannot be correlated by sync service
97
+ */
98
+ export interface ObliviousNullifier {
99
+ /** The nullifier hash */
100
+ nullifier: HexString
101
+ /** Epoch it was derived in */
102
+ epoch: number
103
+ /** Chain ID */
104
+ chainId: ChainId
105
+ }
106
+
107
+ /**
108
+ * Query for oblivious sync - encrypted so service learns nothing
109
+ */
110
+ export interface ObliviousSyncQuery {
111
+ /** Encrypted query (only service can process, learns nothing) */
112
+ encryptedQuery: HexString
113
+ /** Public nonce for query */
114
+ nonce: HexString
115
+ /** Block range being queried */
116
+ blockRange: BlockRange
117
+ /** Query timestamp */
118
+ timestamp: number
119
+ }
120
+
121
+ /**
122
+ * Response from oblivious sync service
123
+ */
124
+ export interface ObliviousSyncResponse {
125
+ /** Encrypted notes found (only querier can decrypt) */
126
+ encryptedNotes: EncryptedNote[]
127
+ /** Merkle proofs for note inclusion */
128
+ merkleProofs: MerkleProof[]
129
+ /** Current sync height */
130
+ syncHeight: bigint
131
+ /** Response timestamp */
132
+ timestamp: number
133
+ /** Query hash for verification */
134
+ queryHash: HexString
135
+ }
136
+
137
+ /**
138
+ * Sync service health information
139
+ */
140
+ export interface SyncServiceHealth {
141
+ /** Is service available */
142
+ available: boolean
143
+ /** Current block height */
144
+ currentHeight: bigint
145
+ /** Chain ID */
146
+ chainId: ChainId
147
+ /** Latency in milliseconds */
148
+ latencyMs: number
149
+ /** Last successful sync timestamp */
150
+ lastSuccessfulSync?: number
151
+ }
152
+
153
+ /**
154
+ * Configuration for oblivious sync
155
+ */
156
+ export interface ObliviousSyncConfig {
157
+ /** Maximum blocks per sync batch */
158
+ maxBatchSize: number
159
+ /** Timeout for sync requests (ms) */
160
+ timeoutMs: number
161
+ /** Number of retries on failure */
162
+ retries: number
163
+ /** Epoch duration for sync randomness (seconds) */
164
+ epochDurationSeconds: number
165
+ /** Enable parallel sync across chains */
166
+ parallelSync: boolean
167
+ }
168
+
169
+ /**
170
+ * Default configuration
171
+ */
172
+ export const DEFAULT_SYNC_CONFIG: ObliviousSyncConfig = {
173
+ maxBatchSize: 1000,
174
+ timeoutMs: 30_000,
175
+ retries: 3,
176
+ epochDurationSeconds: 3600, // 1 hour epochs
177
+ parallelSync: true,
178
+ }
179
+
180
+ // ─── Sync Randomness Management ───────────────────────────────────────────────
181
+
182
+ /**
183
+ * Generate new sync randomness for an epoch
184
+ *
185
+ * @param epoch - Epoch number
186
+ * @param durationSeconds - Duration of epoch in seconds
187
+ * @returns Sync randomness for the epoch
188
+ */
189
+ export function generateSyncRandomness(
190
+ epoch: number,
191
+ durationSeconds: number = DEFAULT_SYNC_CONFIG.epochDurationSeconds
192
+ ): SyncRandomness {
193
+ const value = randomBytes(32)
194
+ const expiresAt = Date.now() + durationSeconds * 1000
195
+
196
+ return {
197
+ value,
198
+ epoch,
199
+ expiresAt,
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Check if sync randomness is still valid
205
+ *
206
+ * @param randomness - Sync randomness to check
207
+ * @returns True if still valid
208
+ */
209
+ export function isSyncRandomnessValid(randomness: SyncRandomness): boolean {
210
+ return Date.now() < randomness.expiresAt
211
+ }
212
+
213
+ /**
214
+ * Get current epoch number
215
+ *
216
+ * @param epochDurationSeconds - Duration of each epoch
217
+ * @returns Current epoch number
218
+ */
219
+ export function getCurrentEpoch(
220
+ epochDurationSeconds: number = DEFAULT_SYNC_CONFIG.epochDurationSeconds
221
+ ): number {
222
+ return Math.floor(Date.now() / (epochDurationSeconds * 1000))
223
+ }
224
+
225
+ // ─── Oblivious Nullifier Derivation ───────────────────────────────────────────
226
+
227
+ /**
228
+ * Domain separator for oblivious nullifier derivation
229
+ */
230
+ const OBLIVIOUS_NULLIFIER_DOMAIN = 'SIP-OBLIVIOUS-NULLIFIER-V1'
231
+
232
+ /**
233
+ * Derive oblivious nullifier from note commitment and sync randomness
234
+ *
235
+ * This is the key primitive that enables oblivious sync:
236
+ *
237
+ * Traditional: nullifier = f(note_commitment, spending_key)
238
+ * → Sync service sees nullifiers you check, can correlate
239
+ *
240
+ * Oblivious: nullifier = f(note_commitment, spending_key, sync_randomness)
241
+ * → Service cannot correlate without sync_randomness (user holds)
242
+ *
243
+ * @param noteCommitment - The note's commitment
244
+ * @param spendingKey - User's spending key
245
+ * @param syncRandomness - Per-epoch sync randomness
246
+ * @returns Oblivious nullifier
247
+ */
248
+ export function deriveObliviousNullifier(
249
+ noteCommitment: HexString,
250
+ spendingKey: HexString,
251
+ syncRandomness: SyncRandomness
252
+ ): ObliviousNullifier {
253
+ // Parse inputs
254
+ const commitmentBytes = hexToBytes(
255
+ noteCommitment.startsWith('0x') ? noteCommitment.slice(2) : noteCommitment
256
+ )
257
+ const keyBytes = hexToBytes(
258
+ spendingKey.startsWith('0x') ? spendingKey.slice(2) : spendingKey
259
+ )
260
+
261
+ try {
262
+ // Build message: domain || commitment || randomness
263
+ const domain = utf8ToBytes(OBLIVIOUS_NULLIFIER_DOMAIN)
264
+ const message = new Uint8Array(domain.length + commitmentBytes.length + syncRandomness.value.length)
265
+ message.set(domain, 0)
266
+ message.set(commitmentBytes, domain.length)
267
+ message.set(syncRandomness.value, domain.length + commitmentBytes.length)
268
+
269
+ // HMAC-SHA256(spendingKey, message)
270
+ const nullifierBytes = hmac(sha256, keyBytes, message)
271
+
272
+ return {
273
+ nullifier: `0x${bytesToHex(nullifierBytes)}` as HexString,
274
+ epoch: syncRandomness.epoch,
275
+ chainId: 'default', // Will be set by caller
276
+ }
277
+ } finally {
278
+ // Wipe sensitive data
279
+ secureWipe(keyBytes)
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Derive traditional nullifier (non-oblivious, for comparison/fallback)
285
+ *
286
+ * @param noteCommitment - The note's commitment
287
+ * @param spendingKey - User's spending key
288
+ * @returns Traditional nullifier
289
+ */
290
+ export function deriveTraditionalNullifier(
291
+ noteCommitment: HexString,
292
+ spendingKey: HexString
293
+ ): HexString {
294
+ const commitmentBytes = hexToBytes(
295
+ noteCommitment.startsWith('0x') ? noteCommitment.slice(2) : noteCommitment
296
+ )
297
+ const keyBytes = hexToBytes(
298
+ spendingKey.startsWith('0x') ? spendingKey.slice(2) : spendingKey
299
+ )
300
+
301
+ try {
302
+ // Simple HMAC-SHA256(spendingKey, commitment)
303
+ const nullifierBytes = hmac(sha256, keyBytes, commitmentBytes)
304
+ return `0x${bytesToHex(nullifierBytes)}` as HexString
305
+ } finally {
306
+ secureWipe(keyBytes)
307
+ }
308
+ }
309
+
310
+ // ─── Oblivious Sync Provider Interface ────────────────────────────────────────
311
+
312
+ /**
313
+ * Oblivious Sync Provider Interface
314
+ *
315
+ * This is the core interface that sync services must implement.
316
+ * The service processes queries but learns NOTHING about:
317
+ * - Which notes belong to the user
318
+ * - When the user spends
319
+ * - Transaction patterns or amounts
320
+ *
321
+ * ## Implementation Requirements
322
+ *
323
+ * 1. **Query Processing**: Must process encrypted queries without decryption
324
+ * 2. **Note Scanning**: Return all potentially matching notes (over-approximate)
325
+ * 3. **No Logging**: Must not log query content or patterns
326
+ * 4. **Stateless**: Should not maintain per-user state
327
+ */
328
+ export interface ObliviousSyncProvider {
329
+ /**
330
+ * Get provider name/identifier
331
+ */
332
+ readonly name: string
333
+
334
+ /**
335
+ * Supported chains
336
+ */
337
+ readonly supportedChains: ChainId[]
338
+
339
+ /**
340
+ * Initialize the provider
341
+ */
342
+ initialize(): Promise<void>
343
+
344
+ /**
345
+ * Check health of sync service for a chain
346
+ *
347
+ * @param chainId - Chain to check
348
+ * @returns Health information
349
+ */
350
+ getHealth(chainId: ChainId): Promise<SyncServiceHealth>
351
+
352
+ /**
353
+ * Scan for notes belonging to a viewing key (oblivious)
354
+ *
355
+ * The service returns ALL notes that COULD belong to the viewing key,
356
+ * without knowing which ones actually do. The user filters locally.
357
+ *
358
+ * @param viewingKeyPublic - Public viewing key (service sees this)
359
+ * @param blockRange - Blocks to scan
360
+ * @returns Encrypted notes that might belong to user
361
+ */
362
+ scanForNotes(
363
+ viewingKeyPublic: HexString,
364
+ blockRange: BlockRange
365
+ ): Promise<EncryptedNote[]>
366
+
367
+ /**
368
+ * Check if nullifiers have been spent (oblivious)
369
+ *
370
+ * The service checks nullifiers but cannot correlate them to specific notes
371
+ * due to the sync randomness included in derivation.
372
+ *
373
+ * @param nullifiers - Oblivious nullifiers to check
374
+ * @returns Map of nullifier → spent status
375
+ */
376
+ checkNullifiers(
377
+ nullifiers: ObliviousNullifier[]
378
+ ): Promise<Map<HexString, boolean>>
379
+
380
+ /**
381
+ * Get Merkle proofs for note inclusion
382
+ *
383
+ * @param commitments - Note commitments to get proofs for
384
+ * @param chainId - Chain ID
385
+ * @returns Merkle proofs for each commitment
386
+ */
387
+ getMerkleProofs(
388
+ commitments: HexString[],
389
+ chainId: ChainId
390
+ ): Promise<Map<HexString, MerkleProof>>
391
+
392
+ /**
393
+ * Execute oblivious sync query
394
+ *
395
+ * This is the fully oblivious query method where the query itself
396
+ * is encrypted and the service learns nothing.
397
+ *
398
+ * @param query - Encrypted oblivious query
399
+ * @returns Sync response with encrypted notes
400
+ */
401
+ executeObliviousQuery(
402
+ query: ObliviousSyncQuery
403
+ ): Promise<ObliviousSyncResponse>
404
+
405
+ /**
406
+ * Get current block height for a chain
407
+ *
408
+ * @param chainId - Chain to query
409
+ * @returns Current block height
410
+ */
411
+ getCurrentHeight(chainId: ChainId): Promise<bigint>
412
+
413
+ /**
414
+ * Subscribe to new notes (streaming)
415
+ *
416
+ * @param viewingKeyPublic - Public viewing key
417
+ * @param chainId - Chain to watch
418
+ * @param callback - Called when new notes found
419
+ * @returns Unsubscribe function
420
+ */
421
+ subscribeToNotes(
422
+ viewingKeyPublic: HexString,
423
+ chainId: ChainId,
424
+ callback: (notes: EncryptedNote[]) => void
425
+ ): () => void
426
+
427
+ /**
428
+ * Shutdown the provider
429
+ */
430
+ shutdown(): Promise<void>
431
+ }
432
+
433
+ // ─── Sync State Management ────────────────────────────────────────────────────
434
+
435
+ /**
436
+ * Sync state for a wallet
437
+ */
438
+ export interface WalletSyncState {
439
+ /** Sync height per chain */
440
+ syncHeights: Map<ChainId, bigint>
441
+ /** Current sync randomness per chain */
442
+ syncRandomness: Map<ChainId, SyncRandomness>
443
+ /** Nullifiers we've checked (for deduplication) */
444
+ checkedNullifiers: Set<HexString>
445
+ /** Pending notes to process */
446
+ pendingNotes: EncryptedNote[]
447
+ /** Last sync timestamp */
448
+ lastSyncTimestamp: number
449
+ }
450
+
451
+ /**
452
+ * Create initial sync state
453
+ *
454
+ * @param chains - Chains to initialize
455
+ * @returns Initial sync state
456
+ */
457
+ export function createSyncState(chains: ChainId[]): WalletSyncState {
458
+ const syncHeights = new Map<ChainId, bigint>()
459
+ const syncRandomness = new Map<ChainId, SyncRandomness>()
460
+ const epoch = getCurrentEpoch()
461
+
462
+ for (const chain of chains) {
463
+ syncHeights.set(chain, 0n)
464
+ syncRandomness.set(chain, generateSyncRandomness(epoch))
465
+ }
466
+
467
+ return {
468
+ syncHeights,
469
+ syncRandomness,
470
+ checkedNullifiers: new Set(),
471
+ pendingNotes: [],
472
+ lastSyncTimestamp: 0,
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Update sync state after successful sync
478
+ *
479
+ * @param state - Current state
480
+ * @param chainId - Chain that was synced
481
+ * @param newHeight - New sync height
482
+ * @param notes - Notes received
483
+ * @returns Updated state
484
+ */
485
+ export function updateSyncState(
486
+ state: WalletSyncState,
487
+ chainId: ChainId,
488
+ newHeight: bigint,
489
+ notes: EncryptedNote[]
490
+ ): WalletSyncState {
491
+ const newSyncHeights = new Map(state.syncHeights)
492
+ newSyncHeights.set(chainId, newHeight)
493
+
494
+ // Rotate randomness if epoch changed
495
+ const currentEpoch = getCurrentEpoch()
496
+ const newRandomness = new Map(state.syncRandomness)
497
+ const existingRandomness = state.syncRandomness.get(chainId)
498
+
499
+ if (!existingRandomness || existingRandomness.epoch !== currentEpoch) {
500
+ newRandomness.set(chainId, generateSyncRandomness(currentEpoch))
501
+ }
502
+
503
+ return {
504
+ ...state,
505
+ syncHeights: newSyncHeights,
506
+ syncRandomness: newRandomness,
507
+ pendingNotes: [...state.pendingNotes, ...notes],
508
+ lastSyncTimestamp: Date.now(),
509
+ }
510
+ }
511
+
512
+ // ─── Viewing Key Integration ──────────────────────────────────────────────────
513
+
514
+ /**
515
+ * Time-windowed viewing key for oblivious sync
516
+ *
517
+ * Enables auditors to see SPECIFIC windows, not ALL history.
518
+ */
519
+ export interface TimeWindowedViewingKey {
520
+ /** The viewing key */
521
+ viewingKey: HexString
522
+ /** Start of valid window (timestamp) */
523
+ windowStart: number
524
+ /** End of valid window (timestamp) */
525
+ windowEnd: number
526
+ /** Epochs covered */
527
+ epochs: number[]
528
+ /** Hash for identification */
529
+ hash: Hash
530
+ }
531
+
532
+ /**
533
+ * Create time-windowed viewing key for selective disclosure
534
+ *
535
+ * @param masterViewingKey - Master viewing key
536
+ * @param startTime - Start of window (timestamp)
537
+ * @param endTime - End of window (timestamp)
538
+ * @param epochDuration - Duration of each epoch
539
+ * @returns Time-windowed viewing key
540
+ */
541
+ export function createTimeWindowedKey(
542
+ masterViewingKey: HexString,
543
+ startTime: number,
544
+ endTime: number,
545
+ epochDuration: number = DEFAULT_SYNC_CONFIG.epochDurationSeconds
546
+ ): TimeWindowedViewingKey {
547
+ // Calculate epochs covered by window
548
+ const startEpoch = Math.floor(startTime / (epochDuration * 1000))
549
+ const endEpoch = Math.floor(endTime / (epochDuration * 1000))
550
+ const epochs: number[] = []
551
+
552
+ for (let e = startEpoch; e <= endEpoch; e++) {
553
+ epochs.push(e)
554
+ }
555
+
556
+ // Derive window-specific key: HMAC(masterKey, startTime || endTime)
557
+ const keyBytes = hexToBytes(
558
+ masterViewingKey.startsWith('0x') ? masterViewingKey.slice(2) : masterViewingKey
559
+ )
560
+
561
+ const windowData = new Uint8Array(16)
562
+ const view = new DataView(windowData.buffer)
563
+ view.setBigUint64(0, BigInt(startTime), false)
564
+ view.setBigUint64(8, BigInt(endTime), false)
565
+
566
+ const derivedKey = hmac(sha256, keyBytes, windowData)
567
+ const keyHash = sha256(derivedKey)
568
+
569
+ // Wipe master key bytes
570
+ secureWipe(keyBytes)
571
+
572
+ return {
573
+ viewingKey: `0x${bytesToHex(derivedKey)}` as HexString,
574
+ windowStart: startTime,
575
+ windowEnd: endTime,
576
+ epochs,
577
+ hash: `0x${bytesToHex(keyHash)}` as Hash,
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Validate that a note falls within viewing key window
583
+ *
584
+ * @param note - Note to check
585
+ * @param windowedKey - Time-windowed viewing key
586
+ * @returns True if note is within window
587
+ */
588
+ export function isNoteInWindow(
589
+ _note: EncryptedNote,
590
+ _windowedKey: TimeWindowedViewingKey
591
+ ): boolean {
592
+ // Check by block number would require block→timestamp mapping
593
+ // For now, we rely on epoch matching during sync
594
+ // Implementation note: Caller should filter by timestamp from note metadata
595
+ return true
596
+ }
597
+
598
+ // ─── Error Types ──────────────────────────────────────────────────────────────
599
+
600
+ /**
601
+ * Oblivious sync error codes
602
+ */
603
+ export enum ObliviousSyncErrorCode {
604
+ PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE',
605
+ CHAIN_NOT_SUPPORTED = 'CHAIN_NOT_SUPPORTED',
606
+ SYNC_TIMEOUT = 'SYNC_TIMEOUT',
607
+ INVALID_RESPONSE = 'INVALID_RESPONSE',
608
+ NULLIFIER_CHECK_FAILED = 'NULLIFIER_CHECK_FAILED',
609
+ MERKLE_PROOF_INVALID = 'MERKLE_PROOF_INVALID',
610
+ RANDOMNESS_EXPIRED = 'RANDOMNESS_EXPIRED',
611
+ }
612
+
613
+ /**
614
+ * Error thrown by oblivious sync operations
615
+ */
616
+ export class ObliviousSyncError extends Error {
617
+ constructor(
618
+ message: string,
619
+ public readonly code: ObliviousSyncErrorCode,
620
+ public readonly context?: Record<string, unknown>
621
+ ) {
622
+ super(message)
623
+ this.name = 'ObliviousSyncError'
624
+ }
625
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Token Registry Module
3
+ *
4
+ * @module tokens
5
+ */
6
+
7
+ export {
8
+ getTokenDecimals,
9
+ getAsset,
10
+ getNativeToken,
11
+ getTokenByAddress,
12
+ isKnownToken,
13
+ getTokensForChain,
14
+ type TokenMetadata,
15
+ } from './registry'