@sip-protocol/sdk 0.2.10 → 0.3.1

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 (49) hide show
  1. package/dist/browser.d.mts +1 -1
  2. package/dist/browser.d.ts +1 -1
  3. package/dist/browser.js +1643 -266
  4. package/dist/browser.mjs +259 -5
  5. package/dist/chunk-4IFOPYJF.mjs +11950 -0
  6. package/dist/chunk-4VJHI66K.mjs +12120 -0
  7. package/dist/chunk-5BAS4D44.mjs +10283 -0
  8. package/dist/chunk-6WOV2YNG.mjs +10179 -0
  9. package/dist/chunk-7IMRM7LN.mjs +12149 -0
  10. package/dist/chunk-DU7LQDD2.mjs +10148 -0
  11. package/dist/{chunk-AV37IZST.mjs → chunk-JNNXNTSS.mjs} +14 -0
  12. package/dist/chunk-KXN6IWL5.mjs +10736 -0
  13. package/dist/chunk-MR7HRCRS.mjs +10165 -0
  14. package/dist/chunk-NDGUWOOZ.mjs +10157 -0
  15. package/dist/chunk-O4Y2ZUDL.mjs +12721 -0
  16. package/dist/chunk-UPTISVCY.mjs +10304 -0
  17. package/dist/chunk-VITVG25F.mjs +982 -0
  18. package/dist/chunk-VXSHK7US.mjs +10158 -0
  19. package/dist/chunk-W3YXIQ7L.mjs +11950 -0
  20. package/dist/chunk-YZCK337Y.mjs +12155 -0
  21. package/dist/index-Ba7njCU3.d.ts +6925 -0
  22. package/dist/index-Co26-vbG.d.mts +6925 -0
  23. package/dist/index-DAgedMrt.d.ts +6927 -0
  24. package/dist/index-DW7AQwcU.d.mts +6927 -0
  25. package/dist/{index-CAhjA4kh.d.mts → index-DqZoHYKI.d.mts} +362 -6
  26. package/dist/index-dTtK_DTl.d.ts +6762 -0
  27. package/dist/index-jnkYu-Z4.d.mts +6762 -0
  28. package/dist/{index-BFOKTz2z.d.ts → index-vB1N1mHd.d.ts} +362 -6
  29. package/dist/index.d.mts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +1340 -199
  32. package/dist/index.mjs +19 -1
  33. package/dist/noir-BHQtFvRk.d.mts +467 -0
  34. package/dist/noir-BHQtFvRk.d.ts +467 -0
  35. package/package.json +14 -14
  36. package/src/index.ts +32 -0
  37. package/src/proofs/worker.ts +240 -4
  38. package/src/settlement/README.md +439 -0
  39. package/src/settlement/backends/direct-chain.ts +569 -0
  40. package/src/settlement/backends/index.ts +22 -0
  41. package/src/settlement/backends/near-intents.ts +480 -0
  42. package/src/settlement/backends/zcash-native.ts +516 -0
  43. package/src/settlement/index.ts +47 -0
  44. package/src/settlement/interface.ts +397 -0
  45. package/src/settlement/registry.ts +269 -0
  46. package/src/settlement/router.ts +383 -0
  47. package/src/zcash/bridge.ts +20 -2
  48. package/src/zcash/swap-service.ts +20 -2
  49. package/LICENSE +0 -21
@@ -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
+ }
@@ -126,7 +126,7 @@ export interface ZcashBridgeConfig {
126
126
  zcashService?: ZcashShieldedService
127
127
  /** Operating mode */
128
128
  mode: 'demo' | 'production'
129
- /** External bridge provider for production */
129
+ /** External bridge provider (required for production mode) */
130
130
  bridgeProvider?: BridgeProvider
131
131
  /** Price feed for conversions */
132
132
  priceFeed?: PriceFeed
@@ -150,7 +150,15 @@ const TOKEN_DECIMALS: Record<string, number> = {
150
150
  }
151
151
 
152
152
  /**
153
- * Mock prices for demo mode (USD)
153
+ * Mock prices for demo mode calculations.
154
+ *
155
+ * ⚠️ DEMO VALUES ONLY - Not for production use!
156
+ *
157
+ * These prices are approximate values for testing and demonstration.
158
+ * They do not reflect real market conditions.
159
+ *
160
+ * @lastUpdated 2024-12-03
161
+ * @note Update quarterly or when prices diverge significantly from market
154
162
  */
155
163
  const MOCK_PRICES: Record<string, number> = {
156
164
  ETH: 2500,
@@ -189,6 +197,8 @@ const ROUTE_CONFIG: Record<string, { minUsd: number; maxUsd: number; feeBps: num
189
197
  *
190
198
  * Bridges tokens from Ethereum, Solana, and other chains to Zcash,
191
199
  * with optional shielding to z-addresses.
200
+ *
201
+ * @throws {IntentError} If production mode is configured without a bridge provider
192
202
  */
193
203
  export class ZcashBridge {
194
204
  private readonly config: Required<Omit<ZcashBridgeConfig, 'zcashService' | 'bridgeProvider' | 'priceFeed'>>
@@ -198,6 +208,14 @@ export class ZcashBridge {
198
208
  private readonly bridgeRequests: Map<string, BridgeResult> = new Map()
199
209
 
200
210
  constructor(config: ZcashBridgeConfig) {
211
+ // Fail-fast validation for production mode
212
+ if (config.mode === 'production' && !config.bridgeProvider) {
213
+ throw new IntentError(
214
+ 'Bridge provider required for production mode',
215
+ ErrorCode.INTENT_INVALID_STATE,
216
+ )
217
+ }
218
+
201
219
  this.config = {
202
220
  mode: config.mode,
203
221
  defaultSlippage: config.defaultSlippage ?? 100,
@@ -55,7 +55,7 @@ export interface ZcashSwapServiceConfig {
55
55
  zcashService?: ZcashShieldedService
56
56
  /** Operating mode */
57
57
  mode: 'demo' | 'production'
58
- /** Bridge provider (for production) */
58
+ /** Bridge provider (required for production mode) */
59
59
  bridgeProvider?: BridgeProvider
60
60
  /** Price feed for quotes */
61
61
  priceFeed?: PriceFeed
@@ -245,7 +245,15 @@ export type ZcashSwapStatus =
245
245
  // ─── Mock Price Data ───────────────────────────────────────────────────────────
246
246
 
247
247
  /**
248
- * Mock prices for demo mode (in USD)
248
+ * Mock prices for demo mode calculations.
249
+ *
250
+ * ⚠️ DEMO VALUES ONLY - Not for production use!
251
+ *
252
+ * These prices are approximate values for testing and demonstration.
253
+ * They do not reflect real market conditions.
254
+ *
255
+ * @lastUpdated 2024-12-03
256
+ * @note Update quarterly or when prices diverge significantly from market
249
257
  */
250
258
  const MOCK_PRICES: Record<string, number> = {
251
259
  ETH: 2500,
@@ -276,6 +284,8 @@ const TOKEN_DECIMALS: Record<string, number> = {
276
284
  * Zcash Swap Service
277
285
  *
278
286
  * Enables cross-chain swaps from ETH/SOL/NEAR to Zcash's shielded pool.
287
+ *
288
+ * @throws {IntentError} If production mode is configured without a bridge provider
279
289
  */
280
290
  export class ZcashSwapService {
281
291
  private readonly config: Required<Omit<ZcashSwapServiceConfig, 'zcashService' | 'bridgeProvider' | 'priceFeed'>>
@@ -286,6 +296,14 @@ export class ZcashSwapService {
286
296
  private readonly swaps: Map<string, ZcashSwapResult> = new Map()
287
297
 
288
298
  constructor(config: ZcashSwapServiceConfig) {
299
+ // Fail-fast validation for production mode
300
+ if (config.mode === 'production' && !config.bridgeProvider) {
301
+ throw new IntentError(
302
+ 'Bridge provider required for production mode',
303
+ ErrorCode.INTENT_INVALID_STATE,
304
+ )
305
+ }
306
+
289
307
  this.config = {
290
308
  mode: config.mode,
291
309
  defaultSlippage: config.defaultSlippage ?? 100, // 1%
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 RECTOR Labs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.