@sip-protocol/sdk 0.7.2 → 0.7.3

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 (50) hide show
  1. package/dist/browser.d.mts +1 -1
  2. package/dist/browser.d.ts +1 -1
  3. package/dist/browser.js +2926 -341
  4. package/dist/browser.mjs +48 -2
  5. package/dist/chunk-2XIVXWHA.mjs +1930 -0
  6. package/dist/chunk-3M3HNQCW.mjs +18253 -0
  7. package/dist/chunk-7RFRWDCW.mjs +1504 -0
  8. package/dist/chunk-F6F73W35.mjs +16166 -0
  9. package/dist/chunk-OFDBEIEK.mjs +16166 -0
  10. package/dist/chunk-SF7YSLF5.mjs +1515 -0
  11. package/dist/chunk-WWUSGOXE.mjs +17129 -0
  12. package/dist/index-8MQz13eJ.d.mts +13746 -0
  13. package/dist/index-B71aXVzk.d.ts +13264 -0
  14. package/dist/index-DIBZHOOQ.d.ts +13746 -0
  15. package/dist/index-pOIIuwfV.d.mts +13264 -0
  16. package/dist/index.d.mts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +2911 -326
  19. package/dist/index.mjs +48 -2
  20. package/dist/solana-4O4K45VU.mjs +46 -0
  21. package/dist/solana-NDABAZ6P.mjs +56 -0
  22. package/dist/solana-ZYO63LY5.mjs +46 -0
  23. package/package.json +2 -2
  24. package/src/chains/solana/index.ts +24 -0
  25. package/src/chains/solana/providers/generic.ts +160 -0
  26. package/src/chains/solana/providers/helius.ts +249 -0
  27. package/src/chains/solana/providers/index.ts +54 -0
  28. package/src/chains/solana/providers/interface.ts +178 -0
  29. package/src/chains/solana/providers/webhook.ts +519 -0
  30. package/src/chains/solana/scan.ts +88 -8
  31. package/src/chains/solana/types.ts +20 -1
  32. package/src/compliance/index.ts +14 -0
  33. package/src/compliance/range-sas.ts +591 -0
  34. package/src/index.ts +99 -0
  35. package/src/privacy-backends/index.ts +86 -0
  36. package/src/privacy-backends/interface.ts +263 -0
  37. package/src/privacy-backends/privacycash-types.ts +278 -0
  38. package/src/privacy-backends/privacycash.ts +460 -0
  39. package/src/privacy-backends/registry.ts +278 -0
  40. package/src/privacy-backends/router.ts +346 -0
  41. package/src/privacy-backends/sip-native.ts +253 -0
  42. package/src/proofs/noir.ts +1 -1
  43. package/src/surveillance/algorithms/address-reuse.ts +143 -0
  44. package/src/surveillance/algorithms/cluster.ts +247 -0
  45. package/src/surveillance/algorithms/exchange.ts +295 -0
  46. package/src/surveillance/algorithms/temporal.ts +337 -0
  47. package/src/surveillance/analyzer.ts +442 -0
  48. package/src/surveillance/index.ts +64 -0
  49. package/src/surveillance/scoring.ts +372 -0
  50. package/src/surveillance/types.ts +264 -0
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Privacy Backend Registry
3
+ *
4
+ * Manages registration and discovery of privacy backends.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const registry = new PrivacyBackendRegistry()
9
+ *
10
+ * // Register backends
11
+ * registry.register(new SIPNativeBackend())
12
+ * registry.register(new PrivacyCashBackend(), { priority: 10 })
13
+ *
14
+ * // Get backends
15
+ * const all = registry.getAll()
16
+ * const byName = registry.get('sip-native')
17
+ * const forChain = registry.getByChain('solana')
18
+ * const forType = registry.getByType('transaction')
19
+ * ```
20
+ */
21
+
22
+ import type { ChainType } from '@sip-protocol/types'
23
+ import type {
24
+ PrivacyBackend,
25
+ BackendType,
26
+ BackendRegistrationOptions,
27
+ RegisteredBackend,
28
+ TransferParams,
29
+ AvailabilityResult,
30
+ } from './interface'
31
+
32
+ /**
33
+ * Default priority for registered backends
34
+ */
35
+ const DEFAULT_PRIORITY = 50
36
+
37
+ /**
38
+ * Registry for managing privacy backends
39
+ *
40
+ * Provides a centralized way to register, discover, and manage
41
+ * different privacy backend implementations.
42
+ */
43
+ export class PrivacyBackendRegistry {
44
+ private backends: Map<string, RegisteredBackend> = new Map()
45
+
46
+ /**
47
+ * Register a privacy backend
48
+ *
49
+ * @param backend - Backend instance to register
50
+ * @param options - Registration options
51
+ * @throws Error if backend with same name exists and override is false
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * registry.register(new SIPNativeBackend())
56
+ * registry.register(new PrivacyCashBackend(), { priority: 100 })
57
+ * ```
58
+ */
59
+ register(
60
+ backend: PrivacyBackend,
61
+ options: BackendRegistrationOptions = {}
62
+ ): void {
63
+ const { override = false, priority = DEFAULT_PRIORITY, enabled = true } = options
64
+
65
+ if (this.backends.has(backend.name) && !override) {
66
+ throw new Error(
67
+ `Backend '${backend.name}' is already registered. ` +
68
+ `Use { override: true } to replace it.`
69
+ )
70
+ }
71
+
72
+ this.backends.set(backend.name, {
73
+ backend,
74
+ priority,
75
+ enabled,
76
+ registeredAt: Date.now(),
77
+ })
78
+ }
79
+
80
+ /**
81
+ * Unregister a backend by name
82
+ *
83
+ * @param name - Backend name to unregister
84
+ * @returns true if backend was removed, false if not found
85
+ */
86
+ unregister(name: string): boolean {
87
+ return this.backends.delete(name)
88
+ }
89
+
90
+ /**
91
+ * Get a backend by name
92
+ *
93
+ * @param name - Backend name
94
+ * @returns Backend instance or undefined if not found
95
+ */
96
+ get(name: string): PrivacyBackend | undefined {
97
+ const entry = this.backends.get(name)
98
+ return entry?.enabled ? entry.backend : undefined
99
+ }
100
+
101
+ /**
102
+ * Check if a backend is registered
103
+ *
104
+ * @param name - Backend name
105
+ * @returns true if registered (regardless of enabled state)
106
+ */
107
+ has(name: string): boolean {
108
+ return this.backends.has(name)
109
+ }
110
+
111
+ /**
112
+ * Get all enabled backends sorted by priority
113
+ *
114
+ * @returns Array of backends (highest priority first)
115
+ */
116
+ getAll(): PrivacyBackend[] {
117
+ return Array.from(this.backends.values())
118
+ .filter(entry => entry.enabled)
119
+ .sort((a, b) => b.priority - a.priority)
120
+ .map(entry => entry.backend)
121
+ }
122
+
123
+ /**
124
+ * Get all registered entries (including disabled)
125
+ *
126
+ * @returns Array of registered backend entries
127
+ */
128
+ getAllEntries(): RegisteredBackend[] {
129
+ return Array.from(this.backends.values())
130
+ .sort((a, b) => b.priority - a.priority)
131
+ }
132
+
133
+ /**
134
+ * Get backends supporting a specific chain
135
+ *
136
+ * @param chain - Chain type to filter by
137
+ * @returns Array of backends supporting the chain
138
+ */
139
+ getByChain(chain: ChainType): PrivacyBackend[] {
140
+ return this.getAll().filter(backend =>
141
+ backend.chains.includes(chain)
142
+ )
143
+ }
144
+
145
+ /**
146
+ * Get backends of a specific type
147
+ *
148
+ * @param type - Backend type to filter by
149
+ * @returns Array of backends of the specified type
150
+ */
151
+ getByType(type: BackendType): PrivacyBackend[] {
152
+ return this.getAll().filter(backend =>
153
+ backend.type === type || backend.type === 'both'
154
+ )
155
+ }
156
+
157
+ /**
158
+ * Get backends that support compliance (viewing keys)
159
+ *
160
+ * @returns Array of compliance-supporting backends
161
+ */
162
+ getCompliant(): PrivacyBackend[] {
163
+ return this.getAll().filter(backend =>
164
+ backend.getCapabilities().complianceSupport
165
+ )
166
+ }
167
+
168
+ /**
169
+ * Find available backends for a transfer
170
+ *
171
+ * @param params - Transfer parameters
172
+ * @returns Array of available backends with availability info
173
+ */
174
+ async findAvailable(
175
+ params: TransferParams
176
+ ): Promise<Array<{ backend: PrivacyBackend; availability: AvailabilityResult }>> {
177
+ const chainBackends = this.getByChain(params.chain)
178
+ const results: Array<{ backend: PrivacyBackend; availability: AvailabilityResult }> = []
179
+
180
+ for (const backend of chainBackends) {
181
+ const availability = await backend.checkAvailability(params)
182
+ if (availability.available) {
183
+ results.push({ backend, availability })
184
+ }
185
+ }
186
+
187
+ return results
188
+ }
189
+
190
+ /**
191
+ * Enable a backend
192
+ *
193
+ * @param name - Backend name
194
+ * @returns true if backend was enabled, false if not found
195
+ */
196
+ enable(name: string): boolean {
197
+ const entry = this.backends.get(name)
198
+ if (entry) {
199
+ entry.enabled = true
200
+ return true
201
+ }
202
+ return false
203
+ }
204
+
205
+ /**
206
+ * Disable a backend
207
+ *
208
+ * @param name - Backend name
209
+ * @returns true if backend was disabled, false if not found
210
+ */
211
+ disable(name: string): boolean {
212
+ const entry = this.backends.get(name)
213
+ if (entry) {
214
+ entry.enabled = false
215
+ return true
216
+ }
217
+ return false
218
+ }
219
+
220
+ /**
221
+ * Set backend priority
222
+ *
223
+ * @param name - Backend name
224
+ * @param priority - New priority value
225
+ * @returns true if priority was set, false if not found
226
+ */
227
+ setPriority(name: string, priority: number): boolean {
228
+ const entry = this.backends.get(name)
229
+ if (entry) {
230
+ entry.priority = priority
231
+ return true
232
+ }
233
+ return false
234
+ }
235
+
236
+ /**
237
+ * Get count of registered backends
238
+ *
239
+ * @param enabledOnly - If true, only count enabled backends
240
+ * @returns Number of backends
241
+ */
242
+ count(enabledOnly: boolean = false): number {
243
+ if (enabledOnly) {
244
+ return Array.from(this.backends.values()).filter(e => e.enabled).length
245
+ }
246
+ return this.backends.size
247
+ }
248
+
249
+ /**
250
+ * Clear all registered backends
251
+ */
252
+ clear(): void {
253
+ this.backends.clear()
254
+ }
255
+
256
+ /**
257
+ * Get backend names
258
+ *
259
+ * @param enabledOnly - If true, only return enabled backend names
260
+ * @returns Array of backend names
261
+ */
262
+ getNames(enabledOnly: boolean = false): string[] {
263
+ if (enabledOnly) {
264
+ return Array.from(this.backends.entries())
265
+ .filter(([, entry]) => entry.enabled)
266
+ .map(([name]) => name)
267
+ }
268
+ return Array.from(this.backends.keys())
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Global default registry instance
274
+ *
275
+ * Use this for simple applications, or create your own instance
276
+ * for more control.
277
+ */
278
+ export const defaultRegistry = new PrivacyBackendRegistry()
@@ -0,0 +1,346 @@
1
+ /**
2
+ * SmartRouter - Privacy Backend Selection
3
+ *
4
+ * Automatically selects the optimal privacy backend based on:
5
+ * - User preferences (privacy, speed, cost, compliance)
6
+ * - Backend capabilities and availability
7
+ * - Transfer parameters
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { SmartRouter, PrivacyBackendRegistry, SIPNativeBackend } from '@sip-protocol/sdk'
12
+ *
13
+ * const registry = new PrivacyBackendRegistry()
14
+ * registry.register(new SIPNativeBackend())
15
+ *
16
+ * const router = new SmartRouter(registry)
17
+ *
18
+ * // Auto-select best backend
19
+ * const result = await router.execute(params, {
20
+ * prioritize: 'compliance',
21
+ * requireViewingKeys: true,
22
+ * })
23
+ *
24
+ * // Or just select without executing
25
+ * const selection = await router.selectBackend(params, config)
26
+ * console.log(`Selected: ${selection.backend.name}`)
27
+ * ```
28
+ */
29
+
30
+ import type {
31
+ PrivacyBackend,
32
+ TransferParams,
33
+ TransactionResult,
34
+ SmartRouterConfig,
35
+ BackendSelectionResult,
36
+ AvailabilityResult,
37
+ } from './interface'
38
+ import { PrivacyBackendRegistry } from './registry'
39
+
40
+ /**
41
+ * Default router configuration
42
+ */
43
+ const DEFAULT_CONFIG: SmartRouterConfig = {
44
+ prioritize: 'privacy',
45
+ requireViewingKeys: false,
46
+ allowComputePrivacy: true,
47
+ }
48
+
49
+ /**
50
+ * Scoring weights for different priorities
51
+ */
52
+ const PRIORITY_WEIGHTS = {
53
+ privacy: {
54
+ hiddenAmount: 25,
55
+ hiddenSender: 25,
56
+ hiddenRecipient: 25,
57
+ hiddenCompute: 10,
58
+ anonymitySet: 15,
59
+ },
60
+ speed: {
61
+ fast: 40,
62
+ medium: 25,
63
+ slow: 10,
64
+ setupRequired: -20,
65
+ },
66
+ cost: {
67
+ baseCost: 50,
68
+ estimatedCost: 50,
69
+ },
70
+ compliance: {
71
+ complianceSupport: 60,
72
+ hiddenAmount: 15,
73
+ hiddenSender: 15,
74
+ hiddenRecipient: 10,
75
+ },
76
+ }
77
+
78
+ /**
79
+ * SmartRouter for automatic backend selection
80
+ *
81
+ * Analyzes available backends and selects the optimal one
82
+ * based on user preferences and transfer requirements.
83
+ */
84
+ export class SmartRouter {
85
+ private registry: PrivacyBackendRegistry
86
+
87
+ /**
88
+ * Create a new SmartRouter
89
+ *
90
+ * @param registry - Backend registry to use for selection
91
+ */
92
+ constructor(registry: PrivacyBackendRegistry) {
93
+ this.registry = registry
94
+ }
95
+
96
+ /**
97
+ * Select the best backend for a transfer
98
+ *
99
+ * @param params - Transfer parameters
100
+ * @param config - Router configuration
101
+ * @returns Selection result with backend and reasoning
102
+ * @throws Error if no suitable backend is found
103
+ */
104
+ async selectBackend(
105
+ params: TransferParams,
106
+ config: Partial<SmartRouterConfig> = {}
107
+ ): Promise<BackendSelectionResult> {
108
+ const fullConfig = { ...DEFAULT_CONFIG, ...config }
109
+
110
+ // Get backends for the chain
111
+ const chainBackends = this.registry.getByChain(params.chain)
112
+
113
+ if (chainBackends.length === 0) {
114
+ throw new Error(
115
+ `No backends available for chain '${params.chain}'. ` +
116
+ `Register a backend that supports this chain.`
117
+ )
118
+ }
119
+
120
+ // Filter and score backends
121
+ const scoredBackends: Array<{
122
+ backend: PrivacyBackend
123
+ availability: AvailabilityResult
124
+ score: number
125
+ reason: string
126
+ }> = []
127
+
128
+ for (const backend of chainBackends) {
129
+ // Check exclusions
130
+ if (fullConfig.excludeBackends?.includes(backend.name)) {
131
+ continue
132
+ }
133
+
134
+ // Check availability
135
+ const availability = await backend.checkAvailability(params)
136
+ if (!availability.available) {
137
+ continue
138
+ }
139
+
140
+ // Check hard requirements
141
+ const capabilities = backend.getCapabilities()
142
+
143
+ // Viewing key requirement
144
+ if (fullConfig.requireViewingKeys && !capabilities.complianceSupport) {
145
+ continue
146
+ }
147
+
148
+ // Anonymity set requirement
149
+ if (
150
+ fullConfig.minAnonymitySet &&
151
+ capabilities.anonymitySet !== undefined &&
152
+ capabilities.anonymitySet < fullConfig.minAnonymitySet
153
+ ) {
154
+ continue
155
+ }
156
+
157
+ // Compute privacy filter
158
+ if (!fullConfig.allowComputePrivacy && backend.type === 'compute') {
159
+ continue
160
+ }
161
+
162
+ // Cost limit
163
+ if (fullConfig.maxCost && availability.estimatedCost) {
164
+ if (availability.estimatedCost > fullConfig.maxCost) {
165
+ continue
166
+ }
167
+ }
168
+
169
+ // Latency limit
170
+ if (fullConfig.maxLatency && availability.estimatedTime) {
171
+ if (availability.estimatedTime > fullConfig.maxLatency) {
172
+ continue
173
+ }
174
+ }
175
+
176
+ // Score this backend
177
+ const { score, reason } = this.scoreBackend(
178
+ backend,
179
+ availability,
180
+ fullConfig
181
+ )
182
+
183
+ scoredBackends.push({
184
+ backend,
185
+ availability,
186
+ score,
187
+ reason,
188
+ })
189
+ }
190
+
191
+ if (scoredBackends.length === 0) {
192
+ throw new Error(
193
+ `No backends meet the requirements for this transfer. ` +
194
+ `Check your router configuration and registered backends.`
195
+ )
196
+ }
197
+
198
+ // Sort by score (descending)
199
+ scoredBackends.sort((a, b) => b.score - a.score)
200
+
201
+ // Preferred backend bonus
202
+ if (fullConfig.preferredBackend) {
203
+ const preferredIndex = scoredBackends.findIndex(
204
+ s => s.backend.name === fullConfig.preferredBackend
205
+ )
206
+ if (preferredIndex > 0) {
207
+ // Move preferred to top if within 10 points of leader
208
+ const preferred = scoredBackends[preferredIndex]
209
+ const leader = scoredBackends[0]
210
+ if (leader.score - preferred.score <= 10) {
211
+ scoredBackends.splice(preferredIndex, 1)
212
+ scoredBackends.unshift(preferred)
213
+ preferred.reason = `Preferred backend (within 10pts of optimal)`
214
+ }
215
+ }
216
+ }
217
+
218
+ const selected = scoredBackends[0]
219
+ const alternatives = scoredBackends.slice(1).map(s => ({
220
+ backend: s.backend,
221
+ score: s.score,
222
+ reason: s.reason,
223
+ }))
224
+
225
+ return {
226
+ backend: selected.backend,
227
+ reason: selected.reason,
228
+ alternatives,
229
+ score: selected.score,
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Execute a transfer using the best available backend
235
+ *
236
+ * @param params - Transfer parameters
237
+ * @param config - Router configuration
238
+ * @returns Transaction result
239
+ */
240
+ async execute(
241
+ params: TransferParams,
242
+ config: Partial<SmartRouterConfig> = {}
243
+ ): Promise<TransactionResult> {
244
+ const selection = await this.selectBackend(params, config)
245
+ return selection.backend.execute(params)
246
+ }
247
+
248
+ /**
249
+ * Get available backends for a transfer (without selecting)
250
+ *
251
+ * @param params - Transfer parameters
252
+ * @returns Array of available backends with scores
253
+ */
254
+ async getAvailableBackends(
255
+ params: TransferParams
256
+ ): Promise<Array<{ backend: PrivacyBackend; availability: AvailabilityResult }>> {
257
+ return this.registry.findAvailable(params)
258
+ }
259
+
260
+ /**
261
+ * Score a backend based on configuration priority
262
+ */
263
+ private scoreBackend(
264
+ backend: PrivacyBackend,
265
+ availability: AvailabilityResult,
266
+ config: SmartRouterConfig
267
+ ): { score: number; reason: string } {
268
+ const capabilities = backend.getCapabilities()
269
+ let score = 0
270
+ const reasons: string[] = []
271
+
272
+ switch (config.prioritize) {
273
+ case 'privacy':
274
+ if (capabilities.hiddenAmount) {
275
+ score += PRIORITY_WEIGHTS.privacy.hiddenAmount
276
+ reasons.push('hidden amounts')
277
+ }
278
+ if (capabilities.hiddenSender) {
279
+ score += PRIORITY_WEIGHTS.privacy.hiddenSender
280
+ reasons.push('hidden sender')
281
+ }
282
+ if (capabilities.hiddenRecipient) {
283
+ score += PRIORITY_WEIGHTS.privacy.hiddenRecipient
284
+ reasons.push('hidden recipient')
285
+ }
286
+ if (capabilities.hiddenCompute) {
287
+ score += PRIORITY_WEIGHTS.privacy.hiddenCompute
288
+ reasons.push('private compute')
289
+ }
290
+ if (capabilities.anonymitySet && capabilities.anonymitySet >= 100) {
291
+ score += PRIORITY_WEIGHTS.privacy.anonymitySet
292
+ reasons.push(`anonymity set: ${capabilities.anonymitySet}`)
293
+ }
294
+ break
295
+
296
+ case 'speed':
297
+ score += PRIORITY_WEIGHTS.speed[capabilities.latencyEstimate]
298
+ reasons.push(`${capabilities.latencyEstimate} latency`)
299
+ if (capabilities.setupRequired) {
300
+ score += PRIORITY_WEIGHTS.speed.setupRequired
301
+ reasons.push('setup required')
302
+ }
303
+ break
304
+
305
+ case 'cost':
306
+ // Lower cost = higher score (invert with log scale)
307
+ if (availability.estimatedCost !== undefined) {
308
+ // Use log scale to handle wide range of costs
309
+ // log10(100) ≈ 2, log10(100000) ≈ 5, log10(1000000) ≈ 6
310
+ const logCost = availability.estimatedCost > BigInt(0)
311
+ ? Math.log10(Number(availability.estimatedCost))
312
+ : 0
313
+ // Max cost assumed around 10^14 (log = 14), gives score 0
314
+ // Min cost around 10^2 (log = 2), gives score ~60
315
+ const costScore = Math.max(0, 70 - logCost * 5)
316
+ score += costScore
317
+ reasons.push(`low cost`)
318
+ }
319
+ break
320
+
321
+ case 'compliance':
322
+ if (capabilities.complianceSupport) {
323
+ score += PRIORITY_WEIGHTS.compliance.complianceSupport
324
+ reasons.push('viewing key support')
325
+ }
326
+ if (capabilities.hiddenAmount) {
327
+ score += PRIORITY_WEIGHTS.compliance.hiddenAmount
328
+ }
329
+ if (capabilities.hiddenSender) {
330
+ score += PRIORITY_WEIGHTS.compliance.hiddenSender
331
+ }
332
+ if (capabilities.hiddenRecipient) {
333
+ score += PRIORITY_WEIGHTS.compliance.hiddenRecipient
334
+ }
335
+ break
336
+ }
337
+
338
+ // Normalize score to 0-100
339
+ score = Math.min(100, Math.max(0, score))
340
+
341
+ return {
342
+ score,
343
+ reason: reasons.length > 0 ? reasons.join(', ') : 'default selection',
344
+ }
345
+ }
346
+ }