@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,480 @@
1
+ /**
2
+ * NEAR Intents Settlement Backend
3
+ *
4
+ * Implements SettlementBackend interface for NEAR 1Click API.
5
+ * Wraps existing NEARIntentsAdapter functionality with standardized interface.
6
+ *
7
+ * @module settlement/backends/near-intents
8
+ */
9
+
10
+ import type {
11
+ SettlementBackend,
12
+ QuoteParams,
13
+ Quote,
14
+ SwapParams,
15
+ SwapResult,
16
+ SwapStatusResponse,
17
+ BackendCapabilities,
18
+ } from '../interface'
19
+ import { SwapStatus } from '../interface'
20
+ import {
21
+ NEARIntentsAdapter,
22
+ type NEARIntentsAdapterConfig,
23
+ type SwapRequest,
24
+ } from '../../adapters/near-intents'
25
+ import {
26
+ type ChainId,
27
+ type Asset,
28
+ PrivacyLevel,
29
+ OneClickSwapStatus,
30
+ } from '@sip-protocol/types'
31
+ import { ValidationError } from '../../errors'
32
+
33
+ /**
34
+ * Map 1Click swap status to SIP SwapStatus
35
+ */
36
+ function mapOneClickStatus(status: OneClickSwapStatus): SwapStatus {
37
+ switch (status) {
38
+ case OneClickSwapStatus.PENDING_DEPOSIT:
39
+ return SwapStatus.PENDING_DEPOSIT
40
+ case OneClickSwapStatus.PROCESSING:
41
+ return SwapStatus.IN_PROGRESS
42
+ case OneClickSwapStatus.SUCCESS:
43
+ return SwapStatus.SUCCESS
44
+ case OneClickSwapStatus.FAILED:
45
+ return SwapStatus.FAILED
46
+ case OneClickSwapStatus.INCOMPLETE_DEPOSIT:
47
+ return SwapStatus.FAILED
48
+ case OneClickSwapStatus.REFUNDED:
49
+ return SwapStatus.REFUNDED
50
+ default:
51
+ return SwapStatus.PENDING_DEPOSIT
52
+ }
53
+ }
54
+
55
+ /**
56
+ * NEAR Intents Settlement Backend
57
+ *
58
+ * Provides privacy-preserving cross-chain swaps via NEAR 1Click API.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const backend = new NEARIntentsBackend({
63
+ * jwtToken: process.env.NEAR_INTENTS_JWT,
64
+ * })
65
+ *
66
+ * // Get quote
67
+ * const quote = await backend.getQuote({
68
+ * fromChain: 'ethereum',
69
+ * toChain: 'solana',
70
+ * fromToken: 'USDC',
71
+ * toToken: 'SOL',
72
+ * amount: 1000000n, // 1 USDC
73
+ * privacyLevel: PrivacyLevel.SHIELDED,
74
+ * recipientMetaAddress: 'sip:solana:0x...:0x...',
75
+ * senderAddress: '0xYourEthAddress',
76
+ * })
77
+ *
78
+ * // Execute swap
79
+ * const result = await backend.executeSwap({
80
+ * quoteId: quote.quoteId,
81
+ * })
82
+ *
83
+ * // Check status
84
+ * const status = await backend.getStatus(result.swapId)
85
+ * ```
86
+ */
87
+ export class NEARIntentsBackend implements SettlementBackend {
88
+ readonly name = 'near-intents' as const
89
+ readonly capabilities: BackendCapabilities
90
+
91
+ private adapter: NEARIntentsAdapter
92
+ private quoteCache: Map<string, QuoteParams>
93
+
94
+ constructor(config: NEARIntentsAdapterConfig = {}) {
95
+ this.adapter = new NEARIntentsAdapter(config)
96
+ this.quoteCache = new Map()
97
+
98
+ // Define backend capabilities
99
+ this.capabilities = {
100
+ supportedSourceChains: [
101
+ 'near',
102
+ 'ethereum',
103
+ 'solana',
104
+ 'polygon',
105
+ 'arbitrum',
106
+ 'optimism',
107
+ 'base',
108
+ 'bitcoin',
109
+ 'zcash',
110
+ ],
111
+ supportedDestinationChains: [
112
+ 'near',
113
+ 'ethereum',
114
+ 'solana',
115
+ 'polygon',
116
+ 'arbitrum',
117
+ 'optimism',
118
+ 'base',
119
+ 'bitcoin',
120
+ 'zcash',
121
+ ],
122
+ supportedPrivacyLevels: [
123
+ PrivacyLevel.TRANSPARENT,
124
+ PrivacyLevel.SHIELDED,
125
+ PrivacyLevel.COMPLIANT,
126
+ ],
127
+ supportsCancellation: false,
128
+ supportsRefunds: true,
129
+ averageExecutionTime: 300, // 5 minutes
130
+ features: ['stealth-addresses', 'cross-chain', 'near-intents'],
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Get quote for a cross-chain swap
136
+ */
137
+ async getQuote(params: QuoteParams): Promise<Quote> {
138
+ // Validate parameters
139
+ this.validateQuoteParams(params)
140
+
141
+ // Build swap request for adapter
142
+ const swapRequest: SwapRequest = {
143
+ requestId: this.generateRequestId(),
144
+ privacyLevel: params.privacyLevel,
145
+ inputAsset: {
146
+ chain: params.fromChain,
147
+ symbol: params.fromToken,
148
+ decimals: 0, // Will be inferred by adapter
149
+ } as Asset,
150
+ outputAsset: {
151
+ chain: params.toChain,
152
+ symbol: params.toToken,
153
+ decimals: 0, // Will be inferred by adapter
154
+ } as Asset,
155
+ inputAmount: params.amount,
156
+ minOutputAmount: params.slippageTolerance
157
+ ? params.amount - (params.amount * BigInt(params.slippageTolerance) / BigInt(10000))
158
+ : undefined,
159
+ }
160
+
161
+ // Prepare swap with adapter
162
+ const prepared = await this.adapter.prepareSwap(
163
+ swapRequest,
164
+ params.recipientMetaAddress,
165
+ params.senderAddress
166
+ )
167
+
168
+ // Get quote from 1Click API
169
+ const oneClickQuote = await this.adapter.getQuote(prepared)
170
+
171
+ // Cache quote params for executeSwap
172
+ this.quoteCache.set(oneClickQuote.quoteId, params)
173
+
174
+ // Convert to standardized Quote format
175
+ const quote: Quote = {
176
+ quoteId: oneClickQuote.quoteId,
177
+ amountIn: oneClickQuote.amountIn,
178
+ amountOut: oneClickQuote.amountOut,
179
+ minAmountOut: oneClickQuote.amountOut, // 1Click doesn't provide separate minAmount
180
+ fees: {
181
+ networkFee: '0', // 1Click doesn't provide separate fee breakdown
182
+ protocolFee: '0',
183
+ totalFeeUSD: oneClickQuote.amountOutUsd,
184
+ },
185
+ depositAddress: oneClickQuote.depositAddress,
186
+ recipientAddress: prepared.stealthAddress?.address ?? params.senderAddress ?? '',
187
+ refundAddress: params.senderAddress,
188
+ expiresAt: new Date(oneClickQuote.deadline).getTime() / 1000,
189
+ estimatedTime: oneClickQuote.timeEstimate,
190
+ metadata: {
191
+ prepared,
192
+ oneClickQuote,
193
+ stealthAddress: prepared.stealthAddress,
194
+ ephemeralPublicKey: prepared.stealthAddress?.ephemeralPublicKey,
195
+ curve: prepared.curve,
196
+ },
197
+ }
198
+
199
+ return quote
200
+ }
201
+
202
+ /**
203
+ * Execute swap using a quote
204
+ */
205
+ async executeSwap(params: SwapParams): Promise<SwapResult> {
206
+ // Validate parameters
207
+ if (!params.quoteId) {
208
+ throw new ValidationError('quoteId is required', 'quoteId')
209
+ }
210
+
211
+ // Retrieve cached quote params
212
+ const quoteParams = this.quoteCache.get(params.quoteId)
213
+ if (!quoteParams) {
214
+ throw new ValidationError(
215
+ 'Quote not found. Please call getQuote() before executeSwap()',
216
+ 'quoteId'
217
+ )
218
+ }
219
+
220
+ // Build swap request
221
+ const swapRequest: SwapRequest = {
222
+ requestId: this.generateRequestId(),
223
+ privacyLevel: quoteParams.privacyLevel,
224
+ inputAsset: {
225
+ chain: quoteParams.fromChain,
226
+ symbol: quoteParams.fromToken,
227
+ decimals: 0,
228
+ } as Asset,
229
+ outputAsset: {
230
+ chain: quoteParams.toChain,
231
+ symbol: quoteParams.toToken,
232
+ decimals: 0,
233
+ } as Asset,
234
+ inputAmount: quoteParams.amount,
235
+ }
236
+
237
+ // Execute swap via adapter
238
+ const adapterResult = await this.adapter.initiateSwap(
239
+ swapRequest,
240
+ quoteParams.recipientMetaAddress,
241
+ quoteParams.senderAddress
242
+ )
243
+
244
+ // If deposit tx provided, notify 1Click
245
+ if (params.depositTxHash) {
246
+ await this.adapter.notifyDeposit(
247
+ adapterResult.depositAddress,
248
+ params.depositTxHash,
249
+ params.nearAccount
250
+ )
251
+ }
252
+
253
+ // Convert to standardized SwapResult format
254
+ const result: SwapResult = {
255
+ swapId: adapterResult.depositAddress, // Use deposit address as swap ID
256
+ status: mapOneClickStatus(adapterResult.status),
257
+ quoteId: params.quoteId,
258
+ depositAddress: adapterResult.depositAddress,
259
+ depositTxHash: params.depositTxHash,
260
+ settlementTxHash: adapterResult.settlementTxHash,
261
+ actualAmountOut: adapterResult.amountOut,
262
+ metadata: {
263
+ adapterResult,
264
+ stealthRecipient: adapterResult.stealthRecipient,
265
+ ephemeralPublicKey: adapterResult.ephemeralPublicKey,
266
+ },
267
+ }
268
+
269
+ return result
270
+ }
271
+
272
+ /**
273
+ * Get current swap status
274
+ */
275
+ async getStatus(swapId: string): Promise<SwapStatusResponse> {
276
+ // swapId is the deposit address
277
+ const oneClickStatus = await this.adapter.getStatus(swapId)
278
+
279
+ // Convert to standardized format
280
+ const status: SwapStatusResponse = {
281
+ swapId,
282
+ status: mapOneClickStatus(oneClickStatus.status),
283
+ quoteId: '', // 1Click status doesn't include quoteId
284
+ depositAddress: swapId,
285
+ amountIn: oneClickStatus.amountIn ?? '0',
286
+ amountOut: oneClickStatus.amountOut ?? '0',
287
+ depositTxHash: oneClickStatus.depositTxHash,
288
+ settlementTxHash: oneClickStatus.settlementTxHash,
289
+ errorMessage: oneClickStatus.error,
290
+ updatedAt: Date.now() / 1000, // 1Click doesn't provide updatedAt
291
+ metadata: {
292
+ oneClickStatus,
293
+ },
294
+ }
295
+
296
+ return status
297
+ }
298
+
299
+ /**
300
+ * Wait for swap completion (optional)
301
+ */
302
+ async waitForCompletion(
303
+ swapId: string,
304
+ options?: {
305
+ interval?: number
306
+ timeout?: number
307
+ onStatusChange?: (status: SwapStatusResponse) => void
308
+ }
309
+ ): Promise<SwapStatusResponse> {
310
+ await this.adapter.waitForCompletion(swapId, {
311
+ interval: options?.interval,
312
+ timeout: options?.timeout,
313
+ onStatus: options?.onStatusChange
314
+ ? (oneClickStatus) => {
315
+ const status: SwapStatusResponse = {
316
+ swapId,
317
+ status: mapOneClickStatus(oneClickStatus.status),
318
+ quoteId: '',
319
+ depositAddress: swapId,
320
+ amountIn: oneClickStatus.amountIn ?? '0',
321
+ amountOut: oneClickStatus.amountOut ?? '0',
322
+ depositTxHash: oneClickStatus.depositTxHash,
323
+ settlementTxHash: oneClickStatus.settlementTxHash,
324
+ errorMessage: oneClickStatus.error,
325
+ updatedAt: Date.now() / 1000,
326
+ metadata: { oneClickStatus },
327
+ }
328
+ options.onStatusChange?.(status)
329
+ }
330
+ : undefined,
331
+ })
332
+
333
+ return this.getStatus(swapId)
334
+ }
335
+
336
+ /**
337
+ * Get dry quote (preview without creating deposit address)
338
+ */
339
+ async getDryQuote(params: QuoteParams): Promise<Quote> {
340
+ // Validate parameters
341
+ this.validateQuoteParams(params)
342
+
343
+ // Build swap request for adapter
344
+ const swapRequest: SwapRequest = {
345
+ requestId: this.generateRequestId(),
346
+ privacyLevel: params.privacyLevel,
347
+ inputAsset: {
348
+ chain: params.fromChain,
349
+ symbol: params.fromToken,
350
+ decimals: 0,
351
+ } as Asset,
352
+ outputAsset: {
353
+ chain: params.toChain,
354
+ symbol: params.toToken,
355
+ decimals: 0,
356
+ } as Asset,
357
+ inputAmount: params.amount,
358
+ }
359
+
360
+ // Prepare swap with adapter
361
+ const prepared = await this.adapter.prepareSwap(
362
+ swapRequest,
363
+ params.recipientMetaAddress,
364
+ params.senderAddress
365
+ )
366
+
367
+ // Get dry quote from 1Click API
368
+ const oneClickQuote = await this.adapter.getDryQuote(prepared)
369
+
370
+ // Convert to standardized Quote format
371
+ const quote: Quote = {
372
+ quoteId: oneClickQuote.quoteId,
373
+ amountIn: oneClickQuote.amountIn,
374
+ amountOut: oneClickQuote.amountOut,
375
+ minAmountOut: oneClickQuote.amountOut,
376
+ fees: {
377
+ networkFee: '0',
378
+ protocolFee: '0',
379
+ totalFeeUSD: oneClickQuote.amountOutUsd,
380
+ },
381
+ depositAddress: oneClickQuote.depositAddress,
382
+ recipientAddress: prepared.stealthAddress?.address ?? params.senderAddress ?? '',
383
+ refundAddress: params.senderAddress,
384
+ expiresAt: new Date(oneClickQuote.deadline).getTime() / 1000,
385
+ estimatedTime: oneClickQuote.timeEstimate,
386
+ metadata: {
387
+ prepared,
388
+ oneClickQuote,
389
+ stealthAddress: prepared.stealthAddress,
390
+ ephemeralPublicKey: prepared.stealthAddress?.ephemeralPublicKey,
391
+ curve: prepared.curve,
392
+ isDryQuote: true,
393
+ },
394
+ }
395
+
396
+ return quote
397
+ }
398
+
399
+ /**
400
+ * Notify backend of deposit transaction
401
+ */
402
+ async notifyDeposit(
403
+ swapId: string,
404
+ txHash: string,
405
+ metadata?: Record<string, unknown>
406
+ ): Promise<void> {
407
+ await this.adapter.notifyDeposit(
408
+ swapId, // swapId is deposit address
409
+ txHash,
410
+ metadata?.nearAccount as string | undefined
411
+ )
412
+ }
413
+
414
+ // ─── Private Methods ──────────────────────────────────────────────────────────
415
+
416
+ private validateQuoteParams(params: QuoteParams): void {
417
+ if (!params.fromChain) {
418
+ throw new ValidationError('fromChain is required', 'fromChain')
419
+ }
420
+ if (!params.toChain) {
421
+ throw new ValidationError('toChain is required', 'toChain')
422
+ }
423
+ if (!params.fromToken) {
424
+ throw new ValidationError('fromToken is required', 'fromToken')
425
+ }
426
+ if (!params.toToken) {
427
+ throw new ValidationError('toToken is required', 'toToken')
428
+ }
429
+ if (!params.amount || params.amount <= BigInt(0)) {
430
+ throw new ValidationError('amount must be greater than 0', 'amount')
431
+ }
432
+ if (!params.privacyLevel) {
433
+ throw new ValidationError('privacyLevel is required', 'privacyLevel')
434
+ }
435
+
436
+ // Validate privacy level requirements
437
+ if (params.privacyLevel !== PrivacyLevel.TRANSPARENT && !params.recipientMetaAddress) {
438
+ throw new ValidationError(
439
+ 'recipientMetaAddress is required for shielded/compliant privacy modes',
440
+ 'recipientMetaAddress'
441
+ )
442
+ }
443
+
444
+ if (params.privacyLevel === PrivacyLevel.TRANSPARENT && !params.senderAddress) {
445
+ throw new ValidationError(
446
+ 'senderAddress is required for transparent mode',
447
+ 'senderAddress'
448
+ )
449
+ }
450
+
451
+ // Validate supported chains
452
+ if (!this.capabilities.supportedSourceChains.includes(params.fromChain)) {
453
+ throw new ValidationError(
454
+ `Source chain ${params.fromChain} is not supported`,
455
+ 'fromChain',
456
+ { supportedChains: this.capabilities.supportedSourceChains }
457
+ )
458
+ }
459
+ if (!this.capabilities.supportedDestinationChains.includes(params.toChain)) {
460
+ throw new ValidationError(
461
+ `Destination chain ${params.toChain} is not supported`,
462
+ 'toChain',
463
+ { supportedChains: this.capabilities.supportedDestinationChains }
464
+ )
465
+ }
466
+ }
467
+
468
+ private generateRequestId(): string {
469
+ return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Create a new NEAR Intents settlement backend
475
+ */
476
+ export function createNEARIntentsBackend(
477
+ config?: NEARIntentsAdapterConfig
478
+ ): NEARIntentsBackend {
479
+ return new NEARIntentsBackend(config)
480
+ }