@sip-protocol/sdk 0.2.9 → 0.3.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 (42) hide show
  1. package/dist/browser.d.mts +100 -2
  2. package/dist/browser.d.ts +100 -2
  3. package/dist/browser.js +2116 -321
  4. package/dist/browser.mjs +516 -16
  5. package/dist/chunk-4IFOPYJF.mjs +11950 -0
  6. package/dist/chunk-7IMRM7LN.mjs +12149 -0
  7. package/dist/chunk-JNNXNTSS.mjs +11034 -0
  8. package/dist/chunk-W3YXIQ7L.mjs +11950 -0
  9. package/dist/chunk-XLEPIR2P.mjs +884 -0
  10. package/dist/index-Ba7njCU3.d.ts +6925 -0
  11. package/dist/index-Co26-vbG.d.mts +6925 -0
  12. package/dist/index-DqZoHYKI.d.mts +6418 -0
  13. package/dist/index-dTtK_DTl.d.ts +6762 -0
  14. package/dist/index-jnkYu-Z4.d.mts +6762 -0
  15. package/dist/index-vB1N1mHd.d.ts +6418 -0
  16. package/dist/index.d.mts +2 -5897
  17. package/dist/index.d.ts +2 -5897
  18. package/dist/index.js +1334 -199
  19. package/dist/index.mjs +19 -1
  20. package/dist/noir-BTyLXLlZ.d.mts +467 -0
  21. package/dist/noir-BTyLXLlZ.d.ts +467 -0
  22. package/dist/proofs/noir.d.mts +1 -1
  23. package/dist/proofs/noir.d.ts +1 -1
  24. package/dist/proofs/noir.js +11 -112
  25. package/dist/proofs/noir.mjs +10 -13
  26. package/package.json +3 -3
  27. package/src/browser.ts +23 -0
  28. package/src/index.ts +32 -0
  29. package/src/proofs/browser-utils.ts +389 -0
  30. package/src/proofs/browser.ts +246 -19
  31. package/src/proofs/circuits/funding_proof.json +1 -1
  32. package/src/proofs/noir.ts +14 -14
  33. package/src/proofs/worker.ts +426 -0
  34. package/src/settlement/README.md +439 -0
  35. package/src/settlement/backends/direct-chain.ts +569 -0
  36. package/src/settlement/backends/index.ts +22 -0
  37. package/src/settlement/backends/near-intents.ts +480 -0
  38. package/src/settlement/backends/zcash-native.ts +516 -0
  39. package/src/settlement/index.ts +47 -0
  40. package/src/settlement/interface.ts +397 -0
  41. package/src/settlement/registry.ts +269 -0
  42. package/src/settlement/router.ts +383 -0
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Smart Router for Optimal Route Selection
3
+ *
4
+ * Queries all compatible backends and finds the best route based on preferences.
5
+ *
6
+ * @module settlement/router
7
+ */
8
+
9
+ import type { ChainId, PrivacyLevel } from '@sip-protocol/types'
10
+ import type {
11
+ SettlementBackend,
12
+ QuoteParams,
13
+ Quote,
14
+ } from './interface'
15
+ import type { SettlementRegistry } from './registry'
16
+ import { ValidationError, NetworkError } from '../errors'
17
+
18
+ /**
19
+ * Route with quote information
20
+ */
21
+ export interface RouteWithQuote {
22
+ /** Backend name */
23
+ backend: string
24
+ /** Quote from backend */
25
+ quote: Quote
26
+ /** Backend instance */
27
+ backendInstance: SettlementBackend
28
+ /** Score for ranking (higher is better) */
29
+ score: number
30
+ }
31
+
32
+ /**
33
+ * Quote comparison result
34
+ */
35
+ export interface QuoteComparison {
36
+ /** All routes with quotes */
37
+ routes: RouteWithQuote[]
38
+ /** Best route by total cost */
39
+ bestByCost: RouteWithQuote | null
40
+ /** Best route by speed */
41
+ bestBySpeed: RouteWithQuote | null
42
+ /** Best route by privacy */
43
+ bestByPrivacy: RouteWithQuote | null
44
+ /** Comparison metadata */
45
+ metadata: {
46
+ /** Total backends queried */
47
+ totalQueried: number
48
+ /** Failed backend queries */
49
+ failures: Array<{ backend: string; error: string }>
50
+ /** Query timestamp */
51
+ queriedAt: number
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Route finding parameters
57
+ */
58
+ export interface FindBestRouteParams {
59
+ /** Source chain and token */
60
+ from: { chain: ChainId; token: string }
61
+ /** Destination chain and token */
62
+ to: { chain: ChainId; token: string }
63
+ /** Amount to swap (in smallest units) */
64
+ amount: bigint
65
+ /** Privacy level */
66
+ privacyLevel: PrivacyLevel
67
+ /** Prefer speed over cost (default: false) */
68
+ preferSpeed?: boolean
69
+ /** Prefer low fees over speed (default: true) */
70
+ preferLowFees?: boolean
71
+ /** Additional quote parameters */
72
+ recipientMetaAddress?: string
73
+ senderAddress?: string
74
+ slippageTolerance?: number
75
+ deadline?: number
76
+ }
77
+
78
+ /**
79
+ * Smart Router for finding optimal settlement routes
80
+ *
81
+ * Queries all compatible backends in parallel and ranks routes by:
82
+ * - Total cost (network + protocol fees)
83
+ * - Execution speed (estimated time)
84
+ * - Privacy support (shielded vs transparent)
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const registry = new SettlementRegistry()
89
+ * registry.register(nearIntentsBackend)
90
+ * registry.register(zcashBackend)
91
+ *
92
+ * const router = new SmartRouter(registry)
93
+ * const routes = await router.findBestRoute({
94
+ * from: { chain: 'ethereum', token: 'USDC' },
95
+ * to: { chain: 'solana', token: 'SOL' },
96
+ * amount: 100_000000n,
97
+ * privacyLevel: PrivacyLevel.SHIELDED,
98
+ * preferLowFees: true
99
+ * })
100
+ *
101
+ * // Get best route
102
+ * const best = routes[0]
103
+ * console.log(`Best backend: ${best.backend}`)
104
+ * console.log(`Cost: ${best.quote.fees.totalFeeUSD} USD`)
105
+ * console.log(`Time: ${best.quote.estimatedTime}s`)
106
+ * ```
107
+ */
108
+ export class SmartRouter {
109
+ constructor(private registry: SettlementRegistry) {}
110
+
111
+ /**
112
+ * Find best routes for a swap
113
+ *
114
+ * Queries all compatible backends in parallel and returns sorted routes.
115
+ *
116
+ * @param params - Route finding parameters
117
+ * @returns Sorted routes (best first)
118
+ * @throws {ValidationError} If no backends support the route
119
+ */
120
+ async findBestRoute(params: FindBestRouteParams): Promise<RouteWithQuote[]> {
121
+ const {
122
+ from,
123
+ to,
124
+ amount,
125
+ privacyLevel,
126
+ preferSpeed = false,
127
+ preferLowFees = true,
128
+ recipientMetaAddress,
129
+ senderAddress,
130
+ slippageTolerance,
131
+ deadline,
132
+ } = params
133
+
134
+ // Validate amount
135
+ if (amount <= 0n) {
136
+ throw new ValidationError('Amount must be greater than zero')
137
+ }
138
+
139
+ // Get all registered backends
140
+ const allBackends = this.registry
141
+ .list()
142
+ .map((name) => this.registry.get(name))
143
+
144
+ // Filter backends that support this route and privacy level
145
+ const compatibleBackends = allBackends.filter((backend) => {
146
+ const { supportedSourceChains, supportedDestinationChains, supportedPrivacyLevels } =
147
+ backend.capabilities
148
+
149
+ const supportsRoute =
150
+ supportedSourceChains.includes(from.chain) &&
151
+ supportedDestinationChains.includes(to.chain)
152
+
153
+ const supportsPrivacy = supportedPrivacyLevels.includes(privacyLevel)
154
+
155
+ return supportsRoute && supportsPrivacy
156
+ })
157
+
158
+ if (compatibleBackends.length === 0) {
159
+ throw new ValidationError(
160
+ `No backend supports route from ${from.chain} to ${to.chain} with privacy level ${privacyLevel}`
161
+ )
162
+ }
163
+
164
+ // Build quote params
165
+ const quoteParams: QuoteParams = {
166
+ fromChain: from.chain,
167
+ toChain: to.chain,
168
+ fromToken: from.token,
169
+ toToken: to.token,
170
+ amount,
171
+ privacyLevel,
172
+ recipientMetaAddress,
173
+ senderAddress,
174
+ slippageTolerance,
175
+ deadline,
176
+ }
177
+
178
+ // Query all compatible backends in parallel
179
+ const quotePromises = compatibleBackends.map(async (backend) => {
180
+ try {
181
+ const quote = await backend.getQuote(quoteParams)
182
+ return {
183
+ backend: backend.name,
184
+ quote,
185
+ backendInstance: backend,
186
+ success: true,
187
+ }
188
+ } catch (error) {
189
+ return {
190
+ backend: backend.name,
191
+ error: error instanceof Error ? error.message : 'Unknown error',
192
+ success: false,
193
+ }
194
+ }
195
+ })
196
+
197
+ const results = await Promise.all(quotePromises)
198
+
199
+ // Filter successful quotes
200
+ const successfulRoutes = results
201
+ .filter((r): r is { backend: string; quote: Quote; backendInstance: SettlementBackend; success: true } => r.success)
202
+ .map((r) => ({
203
+ backend: r.backend,
204
+ quote: r.quote,
205
+ backendInstance: r.backendInstance,
206
+ score: 0, // Will be calculated below
207
+ }))
208
+
209
+ if (successfulRoutes.length === 0) {
210
+ const errors = results
211
+ .filter((r): r is { backend: string; error: string; success: false } => !r.success)
212
+ .map((r) => `${r.backend}: ${r.error}`)
213
+ .join(', ')
214
+
215
+ throw new NetworkError(
216
+ `All backends failed to provide quotes: ${errors}`
217
+ )
218
+ }
219
+
220
+ // Calculate scores and rank
221
+ this.rankRoutes(successfulRoutes, { preferSpeed, preferLowFees })
222
+
223
+ // Sort by score (highest first)
224
+ successfulRoutes.sort((a, b) => b.score - a.score)
225
+
226
+ return successfulRoutes
227
+ }
228
+
229
+ /**
230
+ * Compare quotes from multiple routes side-by-side
231
+ *
232
+ * @param routes - Routes to compare (from findBestRoute)
233
+ * @returns Comparison with best routes by different criteria
234
+ */
235
+ compareQuotes(routes: RouteWithQuote[]): QuoteComparison {
236
+ if (routes.length === 0) {
237
+ return {
238
+ routes: [],
239
+ bestByCost: null,
240
+ bestBySpeed: null,
241
+ bestByPrivacy: null,
242
+ metadata: {
243
+ totalQueried: 0,
244
+ failures: [],
245
+ queriedAt: Date.now(),
246
+ },
247
+ }
248
+ }
249
+
250
+ // Find best by cost (lowest total fee)
251
+ const bestByCost = [...routes].sort((a, b) => {
252
+ const costA = this.calculateTotalCost(a.quote)
253
+ const costB = this.calculateTotalCost(b.quote)
254
+ return costA - costB
255
+ })[0]
256
+
257
+ // Find best by speed (lowest estimated time)
258
+ const bestBySpeed = [...routes].sort((a, b) => {
259
+ const timeA = a.quote.estimatedTime ?? Infinity
260
+ const timeB = b.quote.estimatedTime ?? Infinity
261
+ return timeA - timeB
262
+ })[0]
263
+
264
+ // Find best by privacy (full shielded support)
265
+ const bestByPrivacy = [...routes].find((route) => {
266
+ const { supportedPrivacyLevels } = route.backendInstance.capabilities
267
+ return (
268
+ supportedPrivacyLevels.includes('shielded' as PrivacyLevel) ||
269
+ supportedPrivacyLevels.includes('compliant' as PrivacyLevel)
270
+ )
271
+ }) || routes[0]
272
+
273
+ return {
274
+ routes,
275
+ bestByCost,
276
+ bestBySpeed,
277
+ bestByPrivacy,
278
+ metadata: {
279
+ totalQueried: routes.length,
280
+ failures: [], // Could track from findBestRoute
281
+ queriedAt: Date.now(),
282
+ },
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Rank routes by score
288
+ *
289
+ * Scoring algorithm:
290
+ * - Base score: 100
291
+ * - Cost: Lower fees = higher score (up to +50)
292
+ * - Speed: Faster execution = higher score (up to +30)
293
+ * - Privacy: Better privacy support = higher score (up to +20)
294
+ *
295
+ * @private
296
+ */
297
+ private rankRoutes(
298
+ routes: RouteWithQuote[],
299
+ preferences: { preferSpeed: boolean; preferLowFees: boolean }
300
+ ): void {
301
+ const { preferSpeed, preferLowFees } = preferences
302
+
303
+ // Calculate min/max for normalization
304
+ const costs = routes.map((r) => this.calculateTotalCost(r.quote))
305
+ const times = routes.map((r) => r.quote.estimatedTime ?? Infinity)
306
+
307
+ const minCost = Math.min(...costs)
308
+ const maxCost = Math.max(...costs)
309
+ const minTime = Math.min(...times.filter(t => t !== Infinity))
310
+ const maxTime = Math.max(...times.filter(t => t !== Infinity))
311
+
312
+ // Assign scores
313
+ routes.forEach((route, index) => {
314
+ let score = 100 // Base score
315
+
316
+ // Cost scoring (0-50 points)
317
+ if (maxCost > minCost) {
318
+ const costNormalized = 1 - (costs[index] - minCost) / (maxCost - minCost)
319
+ const costWeight = preferLowFees ? 50 : 30
320
+ score += costNormalized * costWeight
321
+ }
322
+
323
+ // Speed scoring (0-30 points)
324
+ const time = times[index]
325
+ if (time !== Infinity && maxTime > minTime) {
326
+ const speedNormalized = 1 - (time - minTime) / (maxTime - minTime)
327
+ const speedWeight = preferSpeed ? 50 : 30
328
+ score += speedNormalized * speedWeight
329
+ }
330
+
331
+ // Privacy scoring (0-20 points)
332
+ const { supportedPrivacyLevels } = route.backendInstance.capabilities
333
+ if (supportedPrivacyLevels.includes('shielded' as PrivacyLevel)) {
334
+ score += 20
335
+ } else if (supportedPrivacyLevels.includes('compliant' as PrivacyLevel)) {
336
+ score += 10
337
+ }
338
+
339
+ route.score = score
340
+ })
341
+ }
342
+
343
+ /**
344
+ * Calculate total cost from quote
345
+ *
346
+ * Returns total fee in USD if available, otherwise estimates from fees
347
+ *
348
+ * @private
349
+ */
350
+ private calculateTotalCost(quote: Quote): number {
351
+ // Use totalFeeUSD if available
352
+ if (quote.fees.totalFeeUSD) {
353
+ return parseFloat(quote.fees.totalFeeUSD)
354
+ }
355
+
356
+ // Otherwise estimate from network + protocol fees
357
+ // This is a rough estimate - real implementation would need price feeds
358
+ const networkFee = parseFloat(quote.fees.networkFee) || 0
359
+ const protocolFee = parseFloat(quote.fees.protocolFee) || 0
360
+
361
+ return networkFee + protocolFee
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Create a new SmartRouter instance
367
+ *
368
+ * @param registry - Settlement registry with registered backends
369
+ * @returns SmartRouter instance
370
+ *
371
+ * @example
372
+ * ```typescript
373
+ * const registry = new SettlementRegistry()
374
+ * registry.register(nearIntentsBackend)
375
+ * registry.register(zcashBackend)
376
+ *
377
+ * const router = createSmartRouter(registry)
378
+ * const routes = await router.findBestRoute({ ... })
379
+ * ```
380
+ */
381
+ export function createSmartRouter(registry: SettlementRegistry): SmartRouter {
382
+ return new SmartRouter(registry)
383
+ }