@sip-protocol/sdk 0.7.2 → 0.7.4

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
  4. package/dist/browser.d.mts +10 -4
  5. package/dist/browser.d.ts +10 -4
  6. package/dist/browser.js +48874 -18336
  7. package/dist/browser.mjs +674 -48
  8. package/dist/chunk-4GRJ5MAW.mjs +152 -0
  9. package/dist/chunk-5D7A3L3W.mjs +717 -0
  10. package/dist/chunk-64AYA5F5.mjs +7834 -0
  11. package/dist/chunk-GMDGB22A.mjs +379 -0
  12. package/dist/chunk-I534WKN7.mjs +328 -0
  13. package/dist/chunk-IBZVA5Y7.mjs +1003 -0
  14. package/dist/chunk-PRRZAWJE.mjs +223 -0
  15. package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
  16. package/dist/chunk-YWGJ77A2.mjs +33806 -0
  17. package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
  18. package/dist/constants-LHAAUC2T.mjs +51 -0
  19. package/dist/dist-2OGQ7FED.mjs +3957 -0
  20. package/dist/dist-IFHPYLDX.mjs +254 -0
  21. package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
  22. package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
  23. package/dist/index-DXh2IGkz.d.ts +24681 -0
  24. package/dist/index-DeE1ZzA4.d.mts +24681 -0
  25. package/dist/index.d.mts +9 -3
  26. package/dist/index.d.ts +9 -3
  27. package/dist/index.js +48676 -17318
  28. package/dist/index.mjs +583 -19
  29. package/dist/interface-Bf7w1PLW.d.mts +679 -0
  30. package/dist/interface-Bf7w1PLW.d.ts +679 -0
  31. package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
  32. package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
  33. package/dist/proofs/halo2.d.mts +151 -0
  34. package/dist/proofs/halo2.d.ts +151 -0
  35. package/dist/proofs/halo2.js +350 -0
  36. package/dist/proofs/halo2.mjs +11 -0
  37. package/dist/proofs/kimchi.d.mts +160 -0
  38. package/dist/proofs/kimchi.d.ts +160 -0
  39. package/dist/proofs/kimchi.js +431 -0
  40. package/dist/proofs/kimchi.mjs +13 -0
  41. package/dist/proofs/noir.d.mts +1 -1
  42. package/dist/proofs/noir.d.ts +1 -1
  43. package/dist/proofs/noir.js +74 -18
  44. package/dist/proofs/noir.mjs +84 -24
  45. package/dist/solana-U3MEGU7W.mjs +280 -0
  46. package/dist/validity_proof-3POXLPNY.mjs +21 -0
  47. package/package.json +54 -21
  48. package/src/adapters/index.ts +41 -0
  49. package/src/adapters/jupiter.ts +571 -0
  50. package/src/adapters/near-intents.ts +135 -0
  51. package/src/advisor/advisor.ts +653 -0
  52. package/src/advisor/index.ts +54 -0
  53. package/src/advisor/tools.ts +303 -0
  54. package/src/advisor/types.ts +164 -0
  55. package/src/chains/ethereum/announcement.ts +536 -0
  56. package/src/chains/ethereum/bnb-optimizations.ts +474 -0
  57. package/src/chains/ethereum/commitment.ts +522 -0
  58. package/src/chains/ethereum/constants.ts +462 -0
  59. package/src/chains/ethereum/deployment.ts +596 -0
  60. package/src/chains/ethereum/gas-estimation.ts +538 -0
  61. package/src/chains/ethereum/index.ts +268 -0
  62. package/src/chains/ethereum/optimizations.ts +614 -0
  63. package/src/chains/ethereum/privacy-adapter.ts +855 -0
  64. package/src/chains/ethereum/registry.ts +584 -0
  65. package/src/chains/ethereum/rpc.ts +905 -0
  66. package/src/chains/ethereum/stealth.ts +491 -0
  67. package/src/chains/ethereum/token.ts +790 -0
  68. package/src/chains/ethereum/transfer.ts +637 -0
  69. package/src/chains/ethereum/types.ts +456 -0
  70. package/src/chains/ethereum/viewing-key.ts +455 -0
  71. package/src/chains/near/commitment.ts +608 -0
  72. package/src/chains/near/constants.ts +284 -0
  73. package/src/chains/near/function-call.ts +871 -0
  74. package/src/chains/near/history.ts +654 -0
  75. package/src/chains/near/implicit-account.ts +840 -0
  76. package/src/chains/near/index.ts +393 -0
  77. package/src/chains/near/native-transfer.ts +658 -0
  78. package/src/chains/near/nep141.ts +775 -0
  79. package/src/chains/near/privacy-adapter.ts +889 -0
  80. package/src/chains/near/resolver.ts +971 -0
  81. package/src/chains/near/rpc.ts +1016 -0
  82. package/src/chains/near/stealth.ts +419 -0
  83. package/src/chains/near/types.ts +317 -0
  84. package/src/chains/near/viewing-key.ts +876 -0
  85. package/src/chains/solana/anchor-transfer.ts +386 -0
  86. package/src/chains/solana/commitment.ts +577 -0
  87. package/src/chains/solana/constants.ts +126 -12
  88. package/src/chains/solana/ephemeral-keys.ts +543 -0
  89. package/src/chains/solana/index.ts +276 -1
  90. package/src/chains/solana/key-derivation.ts +418 -0
  91. package/src/chains/solana/kit-compat.ts +334 -0
  92. package/src/chains/solana/optimizations.ts +560 -0
  93. package/src/chains/solana/privacy-adapter.ts +605 -0
  94. package/src/chains/solana/providers/generic.ts +201 -0
  95. package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
  96. package/src/chains/solana/providers/helius-enhanced.ts +623 -0
  97. package/src/chains/solana/providers/helius.ts +402 -0
  98. package/src/chains/solana/providers/index.ts +85 -0
  99. package/src/chains/solana/providers/interface.ts +221 -0
  100. package/src/chains/solana/providers/quicknode.ts +409 -0
  101. package/src/chains/solana/providers/triton.ts +426 -0
  102. package/src/chains/solana/providers/webhook.ts +790 -0
  103. package/src/chains/solana/rpc-client.ts +1150 -0
  104. package/src/chains/solana/scan.ts +170 -73
  105. package/src/chains/solana/sol-transfer.ts +732 -0
  106. package/src/chains/solana/spl-transfer.ts +886 -0
  107. package/src/chains/solana/stealth-scanner.ts +703 -0
  108. package/src/chains/solana/sunspot-verifier.ts +453 -0
  109. package/src/chains/solana/transaction-builder.ts +755 -0
  110. package/src/chains/solana/transfer.ts +74 -5
  111. package/src/chains/solana/types.ts +77 -7
  112. package/src/chains/solana/utils.ts +110 -0
  113. package/src/chains/solana/viewing-key.ts +807 -0
  114. package/src/compliance/fireblocks.ts +921 -0
  115. package/src/compliance/index.ts +37 -0
  116. package/src/compliance/range-sas.ts +956 -0
  117. package/src/config/endpoints.ts +100 -0
  118. package/src/crypto.ts +11 -8
  119. package/src/errors.ts +82 -0
  120. package/src/evm/erc4337-relayer.ts +830 -0
  121. package/src/evm/index.ts +47 -0
  122. package/src/fees/calculator.ts +396 -0
  123. package/src/fees/index.ts +87 -0
  124. package/src/fees/near-contract.ts +429 -0
  125. package/src/fees/types.ts +268 -0
  126. package/src/index.ts +785 -1
  127. package/src/intent.ts +6 -3
  128. package/src/logger.ts +324 -0
  129. package/src/network/index.ts +80 -0
  130. package/src/network/proxy.ts +691 -0
  131. package/src/optimizations/index.ts +541 -0
  132. package/src/oracle/types.ts +1 -0
  133. package/src/privacy-backends/arcium-types.ts +727 -0
  134. package/src/privacy-backends/arcium.ts +719 -0
  135. package/src/privacy-backends/combined-privacy.ts +866 -0
  136. package/src/privacy-backends/cspl-token.ts +595 -0
  137. package/src/privacy-backends/cspl-types.ts +512 -0
  138. package/src/privacy-backends/cspl.ts +907 -0
  139. package/src/privacy-backends/health.ts +488 -0
  140. package/src/privacy-backends/inco-types.ts +323 -0
  141. package/src/privacy-backends/inco.ts +616 -0
  142. package/src/privacy-backends/index.ts +336 -0
  143. package/src/privacy-backends/interface.ts +906 -0
  144. package/src/privacy-backends/lru-cache.ts +343 -0
  145. package/src/privacy-backends/magicblock.ts +458 -0
  146. package/src/privacy-backends/mock.ts +258 -0
  147. package/src/privacy-backends/privacycash-types.ts +278 -0
  148. package/src/privacy-backends/privacycash.ts +456 -0
  149. package/src/privacy-backends/private-swap.ts +570 -0
  150. package/src/privacy-backends/rate-limiter.ts +683 -0
  151. package/src/privacy-backends/registry.ts +690 -0
  152. package/src/privacy-backends/router.ts +626 -0
  153. package/src/privacy-backends/shadowwire.ts +449 -0
  154. package/src/privacy-backends/sip-native.ts +256 -0
  155. package/src/privacy-logger.ts +191 -0
  156. package/src/production-safety.ts +373 -0
  157. package/src/proofs/aggregator.ts +1029 -0
  158. package/src/proofs/browser-composer.ts +1150 -0
  159. package/src/proofs/browser.ts +113 -25
  160. package/src/proofs/cache/index.ts +127 -0
  161. package/src/proofs/cache/interface.ts +545 -0
  162. package/src/proofs/cache/key-generator.ts +188 -0
  163. package/src/proofs/cache/lru-cache.ts +481 -0
  164. package/src/proofs/cache/multi-tier-cache.ts +575 -0
  165. package/src/proofs/cache/persistent-cache.ts +788 -0
  166. package/src/proofs/compliance-proof.ts +872 -0
  167. package/src/proofs/composer/base.ts +923 -0
  168. package/src/proofs/composer/index.ts +25 -0
  169. package/src/proofs/composer/interface.ts +518 -0
  170. package/src/proofs/composer/types.ts +383 -0
  171. package/src/proofs/converters/halo2.ts +452 -0
  172. package/src/proofs/converters/index.ts +208 -0
  173. package/src/proofs/converters/interface.ts +363 -0
  174. package/src/proofs/converters/kimchi.ts +462 -0
  175. package/src/proofs/converters/noir.ts +451 -0
  176. package/src/proofs/fallback.ts +888 -0
  177. package/src/proofs/halo2.ts +42 -0
  178. package/src/proofs/index.ts +471 -0
  179. package/src/proofs/interface.ts +13 -0
  180. package/src/proofs/kimchi.ts +42 -0
  181. package/src/proofs/lazy.ts +1004 -0
  182. package/src/proofs/mock.ts +25 -1
  183. package/src/proofs/noir.ts +111 -30
  184. package/src/proofs/orchestrator.ts +960 -0
  185. package/src/proofs/parallel/concurrency.ts +297 -0
  186. package/src/proofs/parallel/dependency-graph.ts +602 -0
  187. package/src/proofs/parallel/executor.ts +420 -0
  188. package/src/proofs/parallel/index.ts +131 -0
  189. package/src/proofs/parallel/interface.ts +685 -0
  190. package/src/proofs/parallel/worker-pool.ts +644 -0
  191. package/src/proofs/providers/halo2.ts +560 -0
  192. package/src/proofs/providers/index.ts +34 -0
  193. package/src/proofs/providers/kimchi.ts +641 -0
  194. package/src/proofs/validator.ts +881 -0
  195. package/src/proofs/verifier.ts +867 -0
  196. package/src/quantum/index.ts +112 -0
  197. package/src/quantum/winternitz-vault.ts +639 -0
  198. package/src/quantum/wots.ts +611 -0
  199. package/src/settlement/backends/direct-chain.ts +1 -0
  200. package/src/settlement/index.ts +9 -0
  201. package/src/settlement/router.ts +732 -46
  202. package/src/solana/index.ts +72 -0
  203. package/src/solana/jito-relayer.ts +687 -0
  204. package/src/solana/noir-verifier-types.ts +430 -0
  205. package/src/solana/noir-verifier.ts +816 -0
  206. package/src/stealth/address-derivation.ts +193 -0
  207. package/src/stealth/ed25519.ts +431 -0
  208. package/src/stealth/index.ts +233 -0
  209. package/src/stealth/meta-address.ts +221 -0
  210. package/src/stealth/secp256k1.ts +368 -0
  211. package/src/stealth/utils.ts +194 -0
  212. package/src/stealth.ts +50 -1504
  213. package/src/surveillance/algorithms/address-reuse.ts +143 -0
  214. package/src/surveillance/algorithms/cluster.ts +247 -0
  215. package/src/surveillance/algorithms/exchange.ts +295 -0
  216. package/src/surveillance/algorithms/temporal.ts +337 -0
  217. package/src/surveillance/analyzer.ts +442 -0
  218. package/src/surveillance/index.ts +64 -0
  219. package/src/surveillance/scoring.ts +372 -0
  220. package/src/surveillance/types.ts +264 -0
  221. package/src/sync/index.ts +106 -0
  222. package/src/sync/manager.ts +504 -0
  223. package/src/sync/mock-provider.ts +318 -0
  224. package/src/sync/oblivious.ts +625 -0
  225. package/src/tokens/index.ts +15 -0
  226. package/src/tokens/registry.ts +301 -0
  227. package/src/utils/deprecation.ts +94 -0
  228. package/src/utils/index.ts +9 -0
  229. package/src/wallet/ethereum/index.ts +68 -0
  230. package/src/wallet/ethereum/metamask-privacy.ts +420 -0
  231. package/src/wallet/ethereum/multi-wallet.ts +646 -0
  232. package/src/wallet/ethereum/privacy-adapter.ts +700 -0
  233. package/src/wallet/ethereum/types.ts +3 -1
  234. package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
  235. package/src/wallet/hardware/index.ts +10 -0
  236. package/src/wallet/hardware/ledger-privacy.ts +414 -0
  237. package/src/wallet/index.ts +71 -0
  238. package/src/wallet/near/adapter.ts +626 -0
  239. package/src/wallet/near/index.ts +86 -0
  240. package/src/wallet/near/meteor-wallet.ts +1153 -0
  241. package/src/wallet/near/my-near-wallet.ts +790 -0
  242. package/src/wallet/near/wallet-selector.ts +702 -0
  243. package/src/wallet/solana/adapter.ts +6 -4
  244. package/src/wallet/solana/index.ts +13 -0
  245. package/src/wallet/solana/privacy-adapter.ts +567 -0
  246. package/src/wallet/sui/types.ts +6 -4
  247. package/src/zcash/rpc-client.ts +13 -6
  248. package/dist/chunk-3INS3PR5.mjs +0 -884
  249. package/dist/chunk-3OVABDRH.mjs +0 -17096
  250. package/dist/chunk-DLDWZFYC.mjs +0 -1495
  251. package/dist/chunk-E6SZWREQ.mjs +0 -57
  252. package/dist/chunk-G33LB27A.mjs +0 -16166
  253. package/dist/chunk-HGU6HZRC.mjs +0 -231
  254. package/dist/chunk-L2K34JCU.mjs +0 -1496
  255. package/dist/chunk-SN4ZDTVW.mjs +0 -16166
  256. package/dist/constants-VOI7BSLK.mjs +0 -27
  257. package/dist/index-BYZbDjal.d.ts +0 -11390
  258. package/dist/index-CHB3KuOB.d.mts +0 -11859
  259. package/dist/index-CzWPI6Le.d.ts +0 -11859
  260. package/dist/index-xbWjohNq.d.mts +0 -11390
  261. package/dist/solana-5EMCTPTS.mjs +0 -46
  262. package/dist/solana-Q4NAVBTS.mjs +0 -46
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Oblivious Sync Module
3
+ *
4
+ * Provides oblivious synchronization where third-party sync services
5
+ * learn NOTHING about user transactions.
6
+ *
7
+ * ## Key Features
8
+ *
9
+ * - **Oblivious Queries**: Service cannot see what you're querying for
10
+ * - **Sync Randomness**: Nullifiers cannot be correlated to notes
11
+ * - **Time-Windowed Disclosure**: Viewing keys limited to specific periods
12
+ * - **Multi-Chain Support**: Sync across multiple chains
13
+ *
14
+ * ## Usage
15
+ *
16
+ * ```typescript
17
+ * import {
18
+ * createMockSyncProvider,
19
+ * createSyncState,
20
+ * deriveObliviousNullifier,
21
+ * generateSyncRandomness,
22
+ * } from '@sip-protocol/sdk'
23
+ *
24
+ * // Initialize sync provider
25
+ * const provider = createMockSyncProvider()
26
+ * await provider.initialize()
27
+ *
28
+ * // Create sync state
29
+ * const state = createSyncState(['ethereum', 'solana'])
30
+ *
31
+ * // Generate oblivious nullifier
32
+ * const randomness = state.syncRandomness.get('ethereum')!
33
+ * const nullifier = deriveObliviousNullifier(
34
+ * noteCommitment,
35
+ * spendingKey,
36
+ * randomness
37
+ * )
38
+ *
39
+ * // Check if spent (service learns nothing)
40
+ * const spent = await provider.checkNullifiers([nullifier])
41
+ * ```
42
+ *
43
+ * @module sync
44
+ */
45
+
46
+ // Core oblivious sync types and functions
47
+ export {
48
+ // Types
49
+ type ChainId,
50
+ type BlockRange,
51
+ type EncryptedNote,
52
+ type MerkleProof,
53
+ type SyncRandomness,
54
+ type ObliviousNullifier,
55
+ type ObliviousSyncQuery,
56
+ type ObliviousSyncResponse,
57
+ type SyncServiceHealth,
58
+ type ObliviousSyncConfig,
59
+ type ObliviousSyncProvider,
60
+ type WalletSyncState,
61
+ type TimeWindowedViewingKey,
62
+
63
+ // Constants
64
+ DEFAULT_SYNC_CONFIG,
65
+
66
+ // Sync randomness functions
67
+ generateSyncRandomness,
68
+ isSyncRandomnessValid,
69
+ getCurrentEpoch,
70
+
71
+ // Nullifier functions
72
+ deriveObliviousNullifier,
73
+ deriveTraditionalNullifier,
74
+
75
+ // Sync state management
76
+ createSyncState,
77
+ updateSyncState,
78
+
79
+ // Viewing key integration
80
+ createTimeWindowedKey,
81
+ isNoteInWindow,
82
+
83
+ // Errors
84
+ ObliviousSyncError,
85
+ ObliviousSyncErrorCode,
86
+ } from './oblivious'
87
+
88
+ // Mock provider for testing
89
+ export {
90
+ MockObliviousSyncProvider,
91
+ createMockSyncProvider,
92
+ DEFAULT_MOCK_CONFIG,
93
+ type MockSyncProviderConfig,
94
+ } from './mock-provider'
95
+
96
+ // Sync manager for orchestration
97
+ export {
98
+ SyncManager,
99
+ createSyncManager,
100
+ DEFAULT_MANAGER_CONFIG,
101
+ type SyncManagerConfig,
102
+ type SyncProgressEvent,
103
+ type SyncCompletionEvent,
104
+ type SyncEventListener,
105
+ type SyncOptions,
106
+ } from './manager'
@@ -0,0 +1,504 @@
1
+ /**
2
+ * Sync Manager
3
+ *
4
+ * Orchestrates oblivious synchronization across multiple providers and chains.
5
+ * Integrates with SIP client for seamless sync experience.
6
+ *
7
+ * @module sync/manager
8
+ */
9
+
10
+ import type { HexString, ViewingKey } from '@sip-protocol/types'
11
+ import type {
12
+ ObliviousSyncProvider,
13
+ ChainId,
14
+ BlockRange,
15
+ EncryptedNote,
16
+ WalletSyncState,
17
+ ObliviousSyncConfig,
18
+ SyncServiceHealth,
19
+ ObliviousNullifier,
20
+ } from './oblivious'
21
+ import {
22
+ createSyncState,
23
+ updateSyncState,
24
+ deriveObliviousNullifier,
25
+ generateSyncRandomness,
26
+ getCurrentEpoch,
27
+ DEFAULT_SYNC_CONFIG,
28
+ ObliviousSyncError,
29
+ ObliviousSyncErrorCode,
30
+ } from './oblivious'
31
+
32
+ // ─── Types ────────────────────────────────────────────────────────────────────
33
+
34
+ /**
35
+ * Sync progress event
36
+ */
37
+ export interface SyncProgressEvent {
38
+ /** Chain being synced */
39
+ chainId: ChainId
40
+ /** Current block being processed */
41
+ currentBlock: bigint
42
+ /** Target block */
43
+ targetBlock: bigint
44
+ /** Percentage complete (0-100) */
45
+ percentComplete: number
46
+ /** Notes found so far */
47
+ notesFound: number
48
+ }
49
+
50
+ /**
51
+ * Sync completion event
52
+ */
53
+ export interface SyncCompletionEvent {
54
+ /** Chains that were synced */
55
+ chains: ChainId[]
56
+ /** Total notes found */
57
+ totalNotes: number
58
+ /** Time taken (ms) */
59
+ durationMs: number
60
+ /** Final sync heights */
61
+ syncHeights: Map<ChainId, bigint>
62
+ }
63
+
64
+ /**
65
+ * Sync event listener
66
+ */
67
+ export type SyncEventListener = {
68
+ onProgress?: (event: SyncProgressEvent) => void
69
+ onComplete?: (event: SyncCompletionEvent) => void
70
+ onError?: (error: Error, chainId: ChainId) => void
71
+ onNotesFound?: (notes: EncryptedNote[], chainId: ChainId) => void
72
+ }
73
+
74
+ /**
75
+ * Sync options for a single sync operation
76
+ */
77
+ export interface SyncOptions {
78
+ /** Chains to sync (default: all) */
79
+ chains?: ChainId[]
80
+ /** Maximum blocks to sync per batch */
81
+ batchSize?: number
82
+ /** Event listener */
83
+ listener?: SyncEventListener
84
+ /** Abort signal for cancellation */
85
+ signal?: AbortSignal
86
+ }
87
+
88
+ /**
89
+ * Sync manager configuration
90
+ */
91
+ export interface SyncManagerConfig extends ObliviousSyncConfig {
92
+ /** Auto-rotate sync randomness on epoch change */
93
+ autoRotateRandomness: boolean
94
+ /** Enable background sync */
95
+ backgroundSync: boolean
96
+ /** Background sync interval (ms) */
97
+ backgroundSyncIntervalMs: number
98
+ }
99
+
100
+ /**
101
+ * Default sync manager config
102
+ */
103
+ export const DEFAULT_MANAGER_CONFIG: SyncManagerConfig = {
104
+ ...DEFAULT_SYNC_CONFIG,
105
+ autoRotateRandomness: true,
106
+ backgroundSync: false,
107
+ backgroundSyncIntervalMs: 60_000, // 1 minute
108
+ }
109
+
110
+ // ─── Sync Manager ─────────────────────────────────────────────────────────────
111
+
112
+ /**
113
+ * Sync Manager
114
+ *
115
+ * Coordinates oblivious synchronization across providers and chains.
116
+ *
117
+ * ## Features
118
+ *
119
+ * - Multi-provider support (fallback on failure)
120
+ * - Multi-chain parallel sync
121
+ * - Automatic randomness rotation
122
+ * - Background sync option
123
+ * - Event-driven progress updates
124
+ *
125
+ * ## Usage
126
+ *
127
+ * ```typescript
128
+ * const manager = new SyncManager(provider)
129
+ * await manager.initialize()
130
+ *
131
+ * // Sync all chains
132
+ * const result = await manager.sync(viewingKey, spendingKey, {
133
+ * listener: {
134
+ * onProgress: (e) => console.log(`${e.percentComplete}%`),
135
+ * onNotesFound: (notes) => console.log(`Found ${notes.length} notes`),
136
+ * }
137
+ * })
138
+ * ```
139
+ */
140
+ export class SyncManager {
141
+ private providers: ObliviousSyncProvider[]
142
+ private state: WalletSyncState | null = null
143
+ private config: SyncManagerConfig
144
+ private backgroundSyncInterval: ReturnType<typeof setInterval> | null = null
145
+ private initialized = false
146
+
147
+ constructor(
148
+ providers: ObliviousSyncProvider | ObliviousSyncProvider[],
149
+ config: Partial<SyncManagerConfig> = {}
150
+ ) {
151
+ this.providers = Array.isArray(providers) ? providers : [providers]
152
+ this.config = { ...DEFAULT_MANAGER_CONFIG, ...config }
153
+
154
+ if (this.providers.length === 0) {
155
+ throw new Error('At least one provider is required')
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Initialize the sync manager
161
+ */
162
+ async initialize(): Promise<void> {
163
+ // Initialize all providers
164
+ await Promise.all(this.providers.map(p => p.initialize()))
165
+
166
+ // Collect all supported chains
167
+ const chains = new Set<ChainId>()
168
+ for (const provider of this.providers) {
169
+ for (const chain of provider.supportedChains) {
170
+ chains.add(chain)
171
+ }
172
+ }
173
+
174
+ // Initialize sync state
175
+ this.state = createSyncState(Array.from(chains))
176
+ this.initialized = true
177
+ }
178
+
179
+ /**
180
+ * Get current sync state
181
+ */
182
+ getState(): WalletSyncState | null {
183
+ return this.state
184
+ }
185
+
186
+ /**
187
+ * Get supported chains
188
+ */
189
+ getSupportedChains(): ChainId[] {
190
+ return this.state ? Array.from(this.state.syncHeights.keys()) : []
191
+ }
192
+
193
+ /**
194
+ * Check health of all providers for a chain
195
+ */
196
+ async getHealth(chainId: ChainId): Promise<SyncServiceHealth[]> {
197
+ this.checkInitialized()
198
+
199
+ const healths: SyncServiceHealth[] = []
200
+ for (const provider of this.providers) {
201
+ if (provider.supportedChains.includes(chainId)) {
202
+ try {
203
+ const health = await provider.getHealth(chainId)
204
+ healths.push(health)
205
+ } catch {
206
+ healths.push({
207
+ available: false,
208
+ currentHeight: 0n,
209
+ chainId,
210
+ latencyMs: 0,
211
+ })
212
+ }
213
+ }
214
+ }
215
+
216
+ return healths
217
+ }
218
+
219
+ /**
220
+ * Sync obliviously
221
+ *
222
+ * Main method for oblivious synchronization. The sync service learns
223
+ * nothing about which notes belong to you or your spending patterns.
224
+ *
225
+ * @param viewingKey - Viewing key for note detection
226
+ * @param spendingKey - Spending key for nullifier derivation
227
+ * @param options - Sync options
228
+ * @returns Notes found during sync
229
+ */
230
+ async sync(
231
+ viewingKey: ViewingKey,
232
+ spendingKey: HexString,
233
+ options: SyncOptions = {}
234
+ ): Promise<EncryptedNote[]> {
235
+ this.checkInitialized()
236
+
237
+ const startTime = Date.now()
238
+ const allNotes: EncryptedNote[] = []
239
+ const chains = options.chains ?? this.getSupportedChains()
240
+
241
+ // Check for abort signal
242
+ if (options.signal?.aborted) {
243
+ throw new Error('Sync aborted')
244
+ }
245
+
246
+ try {
247
+ if (this.config.parallelSync) {
248
+ // Parallel sync across chains
249
+ const results = await Promise.all(
250
+ chains.map(chainId =>
251
+ this.syncChain(chainId, viewingKey, spendingKey, options)
252
+ )
253
+ )
254
+ for (const notes of results) {
255
+ // Use concat to avoid stack overflow with large arrays
256
+ for (const note of notes) {
257
+ allNotes.push(note)
258
+ }
259
+ }
260
+ } else {
261
+ // Sequential sync
262
+ for (const chainId of chains) {
263
+ if (options.signal?.aborted) {
264
+ throw new Error('Sync aborted')
265
+ }
266
+ const notes = await this.syncChain(chainId, viewingKey, spendingKey, options)
267
+ // Use loop to avoid stack overflow with large arrays
268
+ for (const note of notes) {
269
+ allNotes.push(note)
270
+ }
271
+ }
272
+ }
273
+
274
+ // Emit completion event
275
+ options.listener?.onComplete?.({
276
+ chains,
277
+ totalNotes: allNotes.length,
278
+ durationMs: Date.now() - startTime,
279
+ syncHeights: this.state!.syncHeights,
280
+ })
281
+
282
+ return allNotes
283
+ } catch (error) {
284
+ const err = error instanceof Error ? error : new Error(String(error))
285
+ options.listener?.onError?.(err, chains[0] ?? 'unknown')
286
+ throw error
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Check if nullifiers have been spent (obliviously)
292
+ *
293
+ * @param nullifiers - Oblivious nullifiers to check
294
+ * @returns Map of nullifier → spent status
295
+ */
296
+ async checkNullifiersSpent(
297
+ nullifiers: ObliviousNullifier[]
298
+ ): Promise<Map<HexString, boolean>> {
299
+ this.checkInitialized()
300
+
301
+ // Group nullifiers by chain
302
+ const byChain = new Map<ChainId, ObliviousNullifier[]>()
303
+ for (const nullifier of nullifiers) {
304
+ if (!byChain.has(nullifier.chainId)) {
305
+ byChain.set(nullifier.chainId, [])
306
+ }
307
+ byChain.get(nullifier.chainId)!.push(nullifier)
308
+ }
309
+
310
+ // Check each chain with appropriate provider
311
+ const results = new Map<HexString, boolean>()
312
+
313
+ for (const [chainId, chainNullifiers] of byChain) {
314
+ const provider = this.getProviderForChain(chainId)
315
+ if (!provider) {
316
+ throw new ObliviousSyncError(
317
+ `No provider available for chain: ${chainId}`,
318
+ ObliviousSyncErrorCode.CHAIN_NOT_SUPPORTED,
319
+ { chainId }
320
+ )
321
+ }
322
+
323
+ const chainResults = await provider.checkNullifiers(chainNullifiers)
324
+ for (const [nullifier, spent] of chainResults) {
325
+ results.set(nullifier, spent)
326
+ }
327
+ }
328
+
329
+ return results
330
+ }
331
+
332
+ /**
333
+ * Derive oblivious nullifier for a note
334
+ *
335
+ * @param noteCommitment - Note commitment
336
+ * @param spendingKey - Spending key
337
+ * @param chainId - Chain ID
338
+ * @returns Oblivious nullifier
339
+ */
340
+ deriveNullifier(
341
+ noteCommitment: HexString,
342
+ spendingKey: HexString,
343
+ chainId: ChainId
344
+ ): ObliviousNullifier {
345
+ this.checkInitialized()
346
+
347
+ // Get or generate sync randomness for this chain
348
+ let randomness = this.state!.syncRandomness.get(chainId)
349
+ const currentEpoch = getCurrentEpoch(this.config.epochDurationSeconds)
350
+
351
+ if (!randomness || randomness.epoch !== currentEpoch) {
352
+ randomness = generateSyncRandomness(currentEpoch, this.config.epochDurationSeconds)
353
+ this.state!.syncRandomness.set(chainId, randomness)
354
+ }
355
+
356
+ const nullifier = deriveObliviousNullifier(noteCommitment, spendingKey, randomness)
357
+ return { ...nullifier, chainId }
358
+ }
359
+
360
+ /**
361
+ * Start background sync
362
+ *
363
+ * @param viewingKey - Viewing key
364
+ * @param spendingKey - Spending key
365
+ * @param options - Sync options
366
+ */
367
+ startBackgroundSync(
368
+ viewingKey: ViewingKey,
369
+ spendingKey: HexString,
370
+ options: SyncOptions = {}
371
+ ): void {
372
+ this.checkInitialized()
373
+
374
+ if (this.backgroundSyncInterval) {
375
+ this.stopBackgroundSync()
376
+ }
377
+
378
+ this.backgroundSyncInterval = setInterval(
379
+ () => {
380
+ this.sync(viewingKey, spendingKey, options).catch(err => {
381
+ options.listener?.onError?.(err, 'background')
382
+ })
383
+ },
384
+ this.config.backgroundSyncIntervalMs
385
+ )
386
+ }
387
+
388
+ /**
389
+ * Stop background sync
390
+ */
391
+ stopBackgroundSync(): void {
392
+ if (this.backgroundSyncInterval) {
393
+ clearInterval(this.backgroundSyncInterval)
394
+ this.backgroundSyncInterval = null
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Shutdown the sync manager
400
+ */
401
+ async shutdown(): Promise<void> {
402
+ this.stopBackgroundSync()
403
+ await Promise.all(this.providers.map(p => p.shutdown()))
404
+ this.initialized = false
405
+ }
406
+
407
+ // ─── Private Methods ────────────────────────────────────────────────────────
408
+
409
+ private checkInitialized(): void {
410
+ if (!this.initialized || !this.state) {
411
+ throw new Error('SyncManager not initialized. Call initialize() first.')
412
+ }
413
+ }
414
+
415
+ private getProviderForChain(chainId: ChainId): ObliviousSyncProvider | null {
416
+ // Find first available provider for this chain
417
+ for (const provider of this.providers) {
418
+ if (provider.supportedChains.includes(chainId)) {
419
+ return provider
420
+ }
421
+ }
422
+ return null
423
+ }
424
+
425
+ private async syncChain(
426
+ chainId: ChainId,
427
+ viewingKey: ViewingKey,
428
+ _spendingKey: HexString,
429
+ options: SyncOptions
430
+ ): Promise<EncryptedNote[]> {
431
+ const provider = this.getProviderForChain(chainId)
432
+ if (!provider) {
433
+ throw new ObliviousSyncError(
434
+ `No provider available for chain: ${chainId}`,
435
+ ObliviousSyncErrorCode.CHAIN_NOT_SUPPORTED,
436
+ { chainId }
437
+ )
438
+ }
439
+
440
+ // Get current height
441
+ const currentHeight = await provider.getCurrentHeight(chainId)
442
+ const startHeight = this.state!.syncHeights.get(chainId) ?? 0n
443
+ const batchSize = BigInt(options.batchSize ?? this.config.maxBatchSize)
444
+
445
+ const allNotes: EncryptedNote[] = []
446
+ let syncedHeight = startHeight
447
+
448
+ // Sync in batches
449
+ while (syncedHeight < currentHeight) {
450
+ if (options.signal?.aborted) {
451
+ throw new Error('Sync aborted')
452
+ }
453
+
454
+ const endHeight = syncedHeight + batchSize > currentHeight
455
+ ? currentHeight
456
+ : syncedHeight + batchSize
457
+
458
+ const blockRange: BlockRange = {
459
+ startBlock: syncedHeight + 1n,
460
+ endBlock: endHeight,
461
+ chainId,
462
+ }
463
+
464
+ // Scan for notes (obliviously)
465
+ const notes = await provider.scanForNotes(viewingKey.key, blockRange)
466
+ // Use loop to avoid stack overflow with large arrays
467
+ for (const note of notes) {
468
+ allNotes.push(note)
469
+ }
470
+
471
+ // Emit progress
472
+ const percentComplete = Number((endHeight * 100n) / currentHeight)
473
+ options.listener?.onProgress?.({
474
+ chainId,
475
+ currentBlock: endHeight,
476
+ targetBlock: currentHeight,
477
+ percentComplete,
478
+ notesFound: allNotes.length,
479
+ })
480
+
481
+ // Emit notes found
482
+ if (notes.length > 0) {
483
+ options.listener?.onNotesFound?.(notes, chainId)
484
+ }
485
+
486
+ syncedHeight = endHeight
487
+ }
488
+
489
+ // Update state
490
+ this.state = updateSyncState(this.state!, chainId, syncedHeight, allNotes)
491
+
492
+ return allNotes
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Create a sync manager with default configuration
498
+ */
499
+ export function createSyncManager(
500
+ providers: ObliviousSyncProvider | ObliviousSyncProvider[],
501
+ config?: Partial<SyncManagerConfig>
502
+ ): SyncManager {
503
+ return new SyncManager(providers, config)
504
+ }