@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
@@ -181,6 +181,211 @@ export function createWorkerBlobURL(): string {
181
181
  }
182
182
  }
183
183
 
184
+ // Generate validity proof
185
+ async function generateValidityProof(id, params) {
186
+ if (!isReady) {
187
+ sendError(id, new Error('Worker not initialized'));
188
+ return;
189
+ }
190
+
191
+ try {
192
+ sendProgress(id, 'witness', 20, 'Preparing validity witness...');
193
+
194
+ // Import noble crypto for hashing
195
+ const { sha256 } = await import('@noble/hashes/sha256');
196
+
197
+ // Convert inputs to field elements
198
+ const intentHashField = hexToField(params.intentHash);
199
+ const senderAddressField = hexToField(params.senderAddress);
200
+ const senderBlindingField = bytesToField(params.senderBlinding);
201
+ const senderSecretField = bytesToField(params.senderSecret);
202
+ const nonceField = bytesToField(params.nonce);
203
+
204
+ // Compute sender commitment
205
+ const addressBytes = hexToBytes(senderAddressField);
206
+ const blindingBytes = hexToBytes(senderBlindingField.padStart(64, '0'));
207
+ const commitmentPreimage = new Uint8Array([...addressBytes, ...blindingBytes]);
208
+ const commitmentHash = sha256(commitmentPreimage);
209
+ const commitmentX = bytesToHex(commitmentHash.slice(0, 16)).padStart(64, '0');
210
+ const commitmentY = bytesToHex(commitmentHash.slice(16, 32)).padStart(64, '0');
211
+
212
+ // Compute nullifier
213
+ const secretBytes = hexToBytes(senderSecretField.padStart(64, '0'));
214
+ const intentBytes = hexToBytes(intentHashField);
215
+ const nonceBytes = hexToBytes(nonceField.padStart(64, '0'));
216
+ const nullifierPreimage = new Uint8Array([...secretBytes, ...intentBytes, ...nonceBytes]);
217
+ const nullifierHash = sha256(nullifierPreimage);
218
+ const nullifier = bytesToHex(nullifierHash);
219
+
220
+ const signature = Array.from(params.authorizationSignature);
221
+ const messageHash = fieldToBytes32(intentHashField);
222
+
223
+ // Get public key coordinates
224
+ let pubKeyX, pubKeyY;
225
+ if (params.senderPublicKey) {
226
+ pubKeyX = Array.from(params.senderPublicKey.x);
227
+ pubKeyY = Array.from(params.senderPublicKey.y);
228
+ } else {
229
+ // Derive from secret
230
+ const { secp256k1 } = await import('@noble/curves/secp256k1');
231
+ const uncompressedPubKey = secp256k1.getPublicKey(params.senderSecret, false);
232
+ pubKeyX = Array.from(uncompressedPubKey.slice(1, 33));
233
+ pubKeyY = Array.from(uncompressedPubKey.slice(33, 65));
234
+ }
235
+
236
+ const witnessInputs = {
237
+ intent_hash: intentHashField,
238
+ sender_commitment_x: commitmentX,
239
+ sender_commitment_y: commitmentY,
240
+ nullifier: nullifier,
241
+ timestamp: params.timestamp.toString(),
242
+ expiry: params.expiry.toString(),
243
+ sender_address: senderAddressField,
244
+ sender_blinding: senderBlindingField,
245
+ sender_secret: senderSecretField,
246
+ pub_key_x: pubKeyX,
247
+ pub_key_y: pubKeyY,
248
+ signature: signature,
249
+ message_hash: messageHash,
250
+ nonce: nonceField,
251
+ };
252
+
253
+ sendProgress(id, 'witness', 40, 'Executing validity circuit...');
254
+ const { witness } = await validityNoir.execute(witnessInputs);
255
+
256
+ sendProgress(id, 'proving', 60, 'Generating validity proof...');
257
+ const proofData = await validityBackend.generateProof(witness);
258
+
259
+ sendProgress(id, 'complete', 100, 'Validity proof generated');
260
+
261
+ const publicInputs = [
262
+ '0x' + intentHashField,
263
+ '0x' + commitmentX,
264
+ '0x' + commitmentY,
265
+ '0x' + nullifier,
266
+ '0x' + params.timestamp.toString(16).padStart(16, '0'),
267
+ '0x' + params.expiry.toString(16).padStart(16, '0'),
268
+ ];
269
+
270
+ const proof = {
271
+ type: 'validity',
272
+ proof: '0x' + bytesToHex(proofData.proof),
273
+ publicInputs,
274
+ };
275
+
276
+ sendSuccess(id, { proof, publicInputs });
277
+ } catch (error) {
278
+ sendError(id, error);
279
+ }
280
+ }
281
+
282
+ // Generate fulfillment proof
283
+ async function generateFulfillmentProof(id, params) {
284
+ if (!isReady) {
285
+ sendError(id, new Error('Worker not initialized'));
286
+ return;
287
+ }
288
+
289
+ try {
290
+ sendProgress(id, 'witness', 20, 'Preparing fulfillment witness...');
291
+
292
+ // Import noble crypto for hashing
293
+ const { sha256 } = await import('@noble/hashes/sha256');
294
+
295
+ const intentHashField = hexToField(params.intentHash);
296
+ const recipientStealthField = hexToField(params.recipientStealth);
297
+
298
+ // Compute output commitment
299
+ const amountBytes = bigintToBytes(params.outputAmount, 8);
300
+ const blindingBytes = params.outputBlinding.slice(0, 32);
301
+ const outputPreimage = new Uint8Array([...amountBytes, ...blindingBytes]);
302
+ const outputHash = sha256(outputPreimage);
303
+ const commitmentX = bytesToHex(outputHash.slice(0, 16)).padStart(64, '0');
304
+ const commitmentY = bytesToHex(outputHash.slice(16, 32)).padStart(64, '0');
305
+
306
+ const solverSecretField = bytesToField(params.solverSecret);
307
+
308
+ // Compute solver ID
309
+ const solverSecretBytes = hexToBytes(solverSecretField.padStart(64, '0'));
310
+ const solverIdHash = sha256(solverSecretBytes);
311
+ const solverId = bytesToHex(solverIdHash);
312
+
313
+ const outputBlindingField = bytesToField(params.outputBlinding);
314
+
315
+ const attestation = params.oracleAttestation;
316
+ const attestationRecipientField = hexToField(attestation.recipient);
317
+ const attestationTxHashField = hexToField(attestation.txHash);
318
+ const oracleSignature = Array.from(attestation.signature);
319
+
320
+ // Compute oracle message hash
321
+ const recipientBytes = hexToBytes(attestationRecipientField);
322
+ const attestationAmountBytes = bigintToBytes(attestation.amount, 8);
323
+ const txHashBytes = hexToBytes(attestationTxHashField);
324
+ const blockBytes = bigintToBytes(attestation.blockNumber, 8);
325
+ const oraclePreimage = new Uint8Array([
326
+ ...recipientBytes,
327
+ ...attestationAmountBytes,
328
+ ...txHashBytes,
329
+ ...blockBytes,
330
+ ]);
331
+ const oracleMessageHash = Array.from(sha256(oraclePreimage));
332
+
333
+ const oraclePubKeyX = config.oraclePublicKey?.x ?? new Array(32).fill(0);
334
+ const oraclePubKeyY = config.oraclePublicKey?.y ?? new Array(32).fill(0);
335
+
336
+ const witnessInputs = {
337
+ intent_hash: intentHashField,
338
+ output_commitment_x: commitmentX,
339
+ output_commitment_y: commitmentY,
340
+ recipient_stealth: recipientStealthField,
341
+ min_output_amount: params.minOutputAmount.toString(),
342
+ solver_id: solverId,
343
+ fulfillment_time: params.fulfillmentTime.toString(),
344
+ expiry: params.expiry.toString(),
345
+ output_amount: params.outputAmount.toString(),
346
+ output_blinding: outputBlindingField,
347
+ solver_secret: solverSecretField,
348
+ attestation_recipient: attestationRecipientField,
349
+ attestation_amount: attestation.amount.toString(),
350
+ attestation_tx_hash: attestationTxHashField,
351
+ attestation_block: attestation.blockNumber.toString(),
352
+ oracle_signature: oracleSignature,
353
+ oracle_message_hash: oracleMessageHash,
354
+ oracle_pub_key_x: oraclePubKeyX,
355
+ oracle_pub_key_y: oraclePubKeyY,
356
+ };
357
+
358
+ sendProgress(id, 'witness', 40, 'Executing fulfillment circuit...');
359
+ const { witness } = await fulfillmentNoir.execute(witnessInputs);
360
+
361
+ sendProgress(id, 'proving', 60, 'Generating fulfillment proof...');
362
+ const proofData = await fulfillmentBackend.generateProof(witness);
363
+
364
+ sendProgress(id, 'complete', 100, 'Fulfillment proof generated');
365
+
366
+ const publicInputs = [
367
+ '0x' + intentHashField,
368
+ '0x' + commitmentX,
369
+ '0x' + commitmentY,
370
+ '0x' + recipientStealthField,
371
+ '0x' + params.minOutputAmount.toString(16).padStart(16, '0'),
372
+ '0x' + solverId,
373
+ '0x' + params.fulfillmentTime.toString(16).padStart(16, '0'),
374
+ '0x' + params.expiry.toString(16).padStart(16, '0'),
375
+ ];
376
+
377
+ const proof = {
378
+ type: 'fulfillment',
379
+ proof: '0x' + bytesToHex(proofData.proof),
380
+ publicInputs,
381
+ };
382
+
383
+ sendSuccess(id, { proof, publicInputs });
384
+ } catch (error) {
385
+ sendError(id, error);
386
+ }
387
+ }
388
+
184
389
  // Helper functions
185
390
  function bytesToField(bytes) {
186
391
  let result = 0n;
@@ -208,6 +413,39 @@ export function createWorkerBlobURL(): string {
208
413
  return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
209
414
  }
210
415
 
416
+ function hexToBytes(hex) {
417
+ const h = hex.startsWith('0x') ? hex.slice(2) : hex;
418
+ const bytes = new Uint8Array(h.length / 2);
419
+ for (let i = 0; i < h.length; i += 2) {
420
+ bytes[i / 2] = parseInt(h.slice(i, i + 2), 16);
421
+ }
422
+ return bytes;
423
+ }
424
+
425
+ function hexToField(hex) {
426
+ const h = hex.startsWith('0x') ? hex.slice(2) : hex;
427
+ return h.padStart(64, '0');
428
+ }
429
+
430
+ function fieldToBytes32(field) {
431
+ const hex = field.padStart(64, '0');
432
+ const bytes = [];
433
+ for (let i = 0; i < 32; i++) {
434
+ bytes.push(parseInt(hex.slice(i * 2, i * 2 + 2), 16));
435
+ }
436
+ return bytes;
437
+ }
438
+
439
+ function bigintToBytes(value, length) {
440
+ const bytes = new Uint8Array(length);
441
+ let v = value;
442
+ for (let i = length - 1; i >= 0; i--) {
443
+ bytes[i] = Number(v & 0xffn);
444
+ v = v >> 8n;
445
+ }
446
+ return bytes;
447
+ }
448
+
211
449
  // Message handler
212
450
  self.onmessage = async function(event) {
213
451
  const { id, type, params, config: initConfig } = event.data;
@@ -220,12 +458,10 @@ export function createWorkerBlobURL(): string {
220
458
  await generateFundingProof(id, params);
221
459
  break;
222
460
  case 'generateValidityProof':
223
- // TODO: Implement
224
- sendError(id, new Error('Validity proof not yet implemented in worker'));
461
+ await generateValidityProof(id, params);
225
462
  break;
226
463
  case 'generateFulfillmentProof':
227
- // TODO: Implement
228
- sendError(id, new Error('Fulfillment proof not yet implemented in worker'));
464
+ await generateFulfillmentProof(id, params);
229
465
  break;
230
466
  case 'destroy':
231
467
  // Cleanup
@@ -0,0 +1,439 @@
1
+ # Settlement Backend Interface
2
+
3
+ This directory contains the settlement abstraction layer for SIP Protocol, enabling pluggable settlement backends for cross-chain swaps.
4
+
5
+ ## Overview
6
+
7
+ The `SettlementBackend` interface provides a unified API for executing cross-chain swaps through different settlement providers (NEAR Intents, Zcash, THORChain, direct on-chain execution, etc.).
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ ┌─────────────────────────────────────────────────────────┐
13
+ │ SIP SDK (Application Layer) │
14
+ │ • createIntent() │
15
+ │ • getQuotes() │
16
+ │ • execute() │
17
+ └────────────────────┬────────────────────────────────────┘
18
+
19
+
20
+ ┌─────────────────────────────────────────────────────────┐
21
+ │ Settlement Backend Interface (Abstraction Layer) │
22
+ │ • getQuote() │
23
+ │ • executeSwap() │
24
+ │ • getStatus() │
25
+ └────────────────────┬────────────────────────────────────┘
26
+
27
+ ┌───────────┼───────────┬───────────────┐
28
+ ▼ ▼ ▼ ▼
29
+ ┌─────────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────┐
30
+ │ NEAR │ │ Zcash │ │THORChain │ │ Direct Chain │
31
+ │ Intents │ │ Backend │ │ Backend │ │ Backend │
32
+ └─────────────┘ └─────────┘ └──────────┘ └──────────────┘
33
+ ```
34
+
35
+ ## Core Types
36
+
37
+ ### SettlementBackend
38
+
39
+ The main interface that all backends must implement:
40
+
41
+ ```typescript
42
+ interface SettlementBackend {
43
+ readonly name: SettlementBackendName
44
+ readonly capabilities: BackendCapabilities
45
+
46
+ getQuote(params: QuoteParams): Promise<Quote>
47
+ executeSwap(params: SwapParams): Promise<SwapResult>
48
+ getStatus(swapId: string): Promise<SwapStatusResponse>
49
+
50
+ // Optional methods
51
+ cancel?(swapId: string): Promise<void>
52
+ waitForCompletion?(swapId: string, options?): Promise<SwapStatusResponse>
53
+ getDryQuote?(params: QuoteParams): Promise<Quote>
54
+ notifyDeposit?(swapId: string, txHash: string, metadata?): Promise<void>
55
+ }
56
+ ```
57
+
58
+ ### QuoteParams
59
+
60
+ Parameters for requesting a swap quote:
61
+
62
+ ```typescript
63
+ interface QuoteParams {
64
+ fromChain: ChainId
65
+ toChain: ChainId
66
+ fromToken: string
67
+ toToken: string
68
+ amount: bigint
69
+ privacyLevel: PrivacyLevel
70
+ recipientMetaAddress?: StealthMetaAddress | string
71
+ senderAddress?: string
72
+ slippageTolerance?: number
73
+ deadline?: number
74
+ }
75
+ ```
76
+
77
+ ### Quote
78
+
79
+ Quote response with pricing and execution details:
80
+
81
+ ```typescript
82
+ interface Quote {
83
+ quoteId: string
84
+ amountIn: string
85
+ amountOut: string
86
+ minAmountOut: string
87
+ fees: {
88
+ networkFee: string
89
+ protocolFee: string
90
+ totalFeeUSD?: string
91
+ }
92
+ depositAddress: string
93
+ recipientAddress: string
94
+ expiresAt: number
95
+ // ... optional fields
96
+ }
97
+ ```
98
+
99
+ ### SwapStatus
100
+
101
+ Enum tracking swap lifecycle:
102
+
103
+ ```typescript
104
+ enum SwapStatus {
105
+ PENDING_DEPOSIT = 'pending_deposit',
106
+ DEPOSIT_CONFIRMED = 'deposit_confirmed',
107
+ IN_PROGRESS = 'in_progress',
108
+ SUCCESS = 'success',
109
+ FAILED = 'failed',
110
+ CANCELLED = 'cancelled',
111
+ REFUNDING = 'refunding',
112
+ REFUNDED = 'refunded',
113
+ }
114
+ ```
115
+
116
+ ## Implementing a Backend
117
+
118
+ ### 1. Basic Implementation
119
+
120
+ ```typescript
121
+ import type {
122
+ SettlementBackend,
123
+ QuoteParams,
124
+ Quote,
125
+ SwapParams,
126
+ SwapResult,
127
+ SwapStatusResponse,
128
+ BackendCapabilities,
129
+ } from '@sip-protocol/sdk'
130
+
131
+ export class MySettlementBackend implements SettlementBackend {
132
+ readonly name = 'my-backend'
133
+ readonly capabilities: BackendCapabilities = {
134
+ supportedSourceChains: ['ethereum', 'solana'],
135
+ supportedDestinationChains: ['near', 'polygon'],
136
+ supportedPrivacyLevels: [PrivacyLevel.TRANSPARENT, PrivacyLevel.SHIELDED],
137
+ supportsCancellation: false,
138
+ supportsRefunds: true,
139
+ averageExecutionTime: 120, // seconds
140
+ }
141
+
142
+ constructor(private config: MyBackendConfig) {}
143
+
144
+ async getQuote(params: QuoteParams): Promise<Quote> {
145
+ // 1. Validate parameters
146
+ this.validateQuoteParams(params)
147
+
148
+ // 2. Call backend API for quote
149
+ const backendQuote = await this.fetchQuote(params)
150
+
151
+ // 3. Convert to standard Quote format
152
+ return {
153
+ quoteId: backendQuote.id,
154
+ amountIn: params.amount.toString(),
155
+ amountOut: backendQuote.outputAmount,
156
+ minAmountOut: this.calculateMinOutput(
157
+ backendQuote.outputAmount,
158
+ params.slippageTolerance
159
+ ),
160
+ fees: {
161
+ networkFee: backendQuote.networkFee,
162
+ protocolFee: backendQuote.protocolFee,
163
+ },
164
+ depositAddress: backendQuote.depositAddress,
165
+ recipientAddress: backendQuote.recipient,
166
+ expiresAt: Date.now() + 300000, // 5 minutes
167
+ }
168
+ }
169
+
170
+ async executeSwap(params: SwapParams): Promise<SwapResult> {
171
+ // 1. Retrieve quote
172
+ const quote = await this.getQuoteById(params.quoteId)
173
+
174
+ // 2. Execute swap (may involve signing, approvals, etc.)
175
+ const execution = await this.executeBackendSwap(params)
176
+
177
+ // 3. Return result
178
+ return {
179
+ swapId: execution.id,
180
+ status: SwapStatus.PENDING_DEPOSIT,
181
+ quoteId: params.quoteId,
182
+ depositAddress: execution.depositAddress,
183
+ }
184
+ }
185
+
186
+ async getStatus(swapId: string): Promise<SwapStatusResponse> {
187
+ // Query backend for current status
188
+ const backendStatus = await this.fetchStatus(swapId)
189
+
190
+ return {
191
+ swapId,
192
+ status: this.mapStatus(backendStatus.status),
193
+ quoteId: backendStatus.quoteId,
194
+ depositAddress: backendStatus.depositAddress,
195
+ amountIn: backendStatus.amountIn,
196
+ amountOut: backendStatus.amountOut,
197
+ depositTxHash: backendStatus.depositTx,
198
+ settlementTxHash: backendStatus.settlementTx,
199
+ updatedAt: backendStatus.lastUpdated,
200
+ }
201
+ }
202
+
203
+ // Helper methods
204
+ private validateQuoteParams(params: QuoteParams): void {
205
+ if (!this.capabilities.supportedSourceChains.includes(params.fromChain)) {
206
+ throw new ValidationError(`Chain ${params.fromChain} not supported`)
207
+ }
208
+ // ... more validation
209
+ }
210
+
211
+ private mapStatus(backendStatus: string): SwapStatus {
212
+ // Map backend-specific status to SwapStatus enum
213
+ const statusMap = {
214
+ 'waiting': SwapStatus.PENDING_DEPOSIT,
215
+ 'processing': SwapStatus.IN_PROGRESS,
216
+ 'completed': SwapStatus.SUCCESS,
217
+ 'failed': SwapStatus.FAILED,
218
+ }
219
+ return statusMap[backendStatus] ?? SwapStatus.FAILED
220
+ }
221
+ }
222
+ ```
223
+
224
+ ### 2. Optional Methods
225
+
226
+ Implement optional methods for enhanced functionality:
227
+
228
+ ```typescript
229
+ export class MySettlementBackend implements SettlementBackend {
230
+ // ... required methods ...
231
+
232
+ async cancel(swapId: string): Promise<void> {
233
+ if (!this.capabilities.supportsCancellation) {
234
+ throw new ProofError('Cancellation not supported')
235
+ }
236
+
237
+ const status = await this.getStatus(swapId)
238
+ if (status.status !== SwapStatus.PENDING_DEPOSIT) {
239
+ throw new ValidationError('Can only cancel pending swaps')
240
+ }
241
+
242
+ await this.backendCancelSwap(swapId)
243
+ }
244
+
245
+ async waitForCompletion(
246
+ swapId: string,
247
+ options?: { interval?: number; timeout?: number; onStatusChange?: (status: SwapStatusResponse) => void }
248
+ ): Promise<SwapStatusResponse> {
249
+ const interval = options?.interval ?? 5000
250
+ const timeout = options?.timeout ?? 600000
251
+ const startTime = Date.now()
252
+
253
+ while (Date.now() - startTime < timeout) {
254
+ const status = await this.getStatus(swapId)
255
+ options?.onStatusChange?.(status)
256
+
257
+ if (
258
+ status.status === SwapStatus.SUCCESS ||
259
+ status.status === SwapStatus.FAILED ||
260
+ status.status === SwapStatus.REFUNDED
261
+ ) {
262
+ return status
263
+ }
264
+
265
+ await new Promise(resolve => setTimeout(resolve, interval))
266
+ }
267
+
268
+ throw new NetworkError('Swap timeout')
269
+ }
270
+
271
+ async getDryQuote(params: QuoteParams): Promise<Quote> {
272
+ // Return quote without creating deposit address
273
+ return this.getQuote({ ...params, dry: true })
274
+ }
275
+
276
+ async notifyDeposit(
277
+ swapId: string,
278
+ txHash: string,
279
+ metadata?: Record<string, unknown>
280
+ ): Promise<void> {
281
+ await this.backendNotifyDeposit(swapId, txHash, metadata)
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### 3. Factory Function
287
+
288
+ Provide a factory function for easy instantiation:
289
+
290
+ ```typescript
291
+ export function createMyBackend(config: MyBackendConfig): SettlementBackend {
292
+ return new MySettlementBackend(config)
293
+ }
294
+ ```
295
+
296
+ ### 4. Registry Entry
297
+
298
+ Create a registry entry for discoverability:
299
+
300
+ ```typescript
301
+ export const myBackendRegistry: SettlementBackendRegistry = {
302
+ name: 'my-backend',
303
+ factory: createMyBackend,
304
+ displayName: 'My Settlement Backend',
305
+ description: 'Fast and reliable cross-chain swaps',
306
+ homepage: 'https://mybackend.com',
307
+ docs: 'https://docs.mybackend.com',
308
+ }
309
+ ```
310
+
311
+ ## Privacy Support
312
+
313
+ Backends should handle privacy levels appropriately:
314
+
315
+ ### Transparent Mode
316
+
317
+ No privacy features required. Use sender's address directly.
318
+
319
+ ```typescript
320
+ if (params.privacyLevel === PrivacyLevel.TRANSPARENT) {
321
+ // Use senderAddress as both sender and recipient
322
+ recipientAddress = params.senderAddress
323
+ }
324
+ ```
325
+
326
+ ### Shielded Mode
327
+
328
+ Generate stealth address for recipient:
329
+
330
+ ```typescript
331
+ if (params.privacyLevel === PrivacyLevel.SHIELDED) {
332
+ // Generate stealth address from recipientMetaAddress
333
+ const { stealthAddress, ephemeralPublicKey } = generateStealthAddress(
334
+ params.recipientMetaAddress
335
+ )
336
+
337
+ recipientAddress = stealthAddress
338
+ // Store ephemeralPublicKey in metadata for recipient recovery
339
+ }
340
+ ```
341
+
342
+ ### Compliant Mode
343
+
344
+ Same as shielded, but include viewing key hash for auditors:
345
+
346
+ ```typescript
347
+ if (params.privacyLevel === PrivacyLevel.COMPLIANT) {
348
+ // Generate stealth address + viewing key hash
349
+ const { stealthAddress, viewingKeyHash } = generateCompliantAddress(
350
+ params.recipientMetaAddress,
351
+ params.viewingKey
352
+ )
353
+
354
+ recipientAddress = stealthAddress
355
+ // Include viewingKeyHash in metadata for auditors
356
+ }
357
+ ```
358
+
359
+ ## Error Handling
360
+
361
+ Use standard SIP errors:
362
+
363
+ ```typescript
364
+ import { ValidationError, NetworkError, ProofError } from '@sip-protocol/sdk'
365
+
366
+ // Invalid parameters
367
+ throw new ValidationError('Invalid amount', 'amount', { min: 0, received: -1 })
368
+
369
+ // API/network issues
370
+ throw new NetworkError('Backend API unavailable', { status: 503 })
371
+
372
+ // Unsupported features
373
+ throw new ProofError('Feature not supported by this backend')
374
+ ```
375
+
376
+ ## Testing
377
+
378
+ Write comprehensive tests for your backend:
379
+
380
+ ```typescript
381
+ import { describe, it, expect } from 'vitest'
382
+ import { PrivacyLevel } from '@sip-protocol/types'
383
+ import { MySettlementBackend } from './my-backend'
384
+
385
+ describe('MySettlementBackend', () => {
386
+ it('should implement SettlementBackend interface', () => {
387
+ const backend = new MySettlementBackend(config)
388
+
389
+ expect(backend.name).toBe('my-backend')
390
+ expect(backend.capabilities).toBeDefined()
391
+ expect(typeof backend.getQuote).toBe('function')
392
+ expect(typeof backend.executeSwap).toBe('function')
393
+ expect(typeof backend.getStatus).toBe('function')
394
+ })
395
+
396
+ it('should get quote for valid params', async () => {
397
+ const backend = new MySettlementBackend(config)
398
+
399
+ const quote = await backend.getQuote({
400
+ fromChain: 'ethereum',
401
+ toChain: 'near',
402
+ fromToken: 'USDC',
403
+ toToken: 'NEAR',
404
+ amount: 1000000n,
405
+ privacyLevel: PrivacyLevel.TRANSPARENT,
406
+ })
407
+
408
+ expect(quote.quoteId).toBeDefined()
409
+ expect(quote.depositAddress).toBeDefined()
410
+ expect(BigInt(quote.amountOut)).toBeGreaterThan(0n)
411
+ })
412
+
413
+ // ... more tests
414
+ })
415
+ ```
416
+
417
+ ## Examples
418
+
419
+ See existing implementations:
420
+
421
+ - `packages/sdk/src/adapters/near-intents.ts` - NEAR Intents adapter (reference implementation)
422
+ - `packages/sdk/src/zcash/swap-service.ts` - Zcash swap service (privacy-focused)
423
+
424
+ ## Roadmap
425
+
426
+ Planned backends for M11-M12 milestones:
427
+
428
+ - [x] NEAR Intents (M1-M8)
429
+ - [ ] Zcash Settlement Backend (M9)
430
+ - [ ] THORChain Backend (M10)
431
+ - [ ] Direct Chain Backend (M11)
432
+ - [ ] Mina Protocol Backend (M12)
433
+
434
+ ## Resources
435
+
436
+ - [SIP Protocol Documentation](https://docs.sip-protocol.org)
437
+ - [NEAR Intents API](https://1click.chaindefuser.com)
438
+ - [THORChain Docs](https://docs.thorchain.org)
439
+ - [Zcash Shielded Transactions](https://z.cash/technology/zksnarks/)