@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.
- package/dist/browser.d.mts +100 -2
- package/dist/browser.d.ts +100 -2
- package/dist/browser.js +2116 -321
- package/dist/browser.mjs +516 -16
- package/dist/chunk-4IFOPYJF.mjs +11950 -0
- package/dist/chunk-7IMRM7LN.mjs +12149 -0
- package/dist/chunk-JNNXNTSS.mjs +11034 -0
- package/dist/chunk-W3YXIQ7L.mjs +11950 -0
- package/dist/chunk-XLEPIR2P.mjs +884 -0
- package/dist/index-Ba7njCU3.d.ts +6925 -0
- package/dist/index-Co26-vbG.d.mts +6925 -0
- package/dist/index-DqZoHYKI.d.mts +6418 -0
- package/dist/index-dTtK_DTl.d.ts +6762 -0
- package/dist/index-jnkYu-Z4.d.mts +6762 -0
- package/dist/index-vB1N1mHd.d.ts +6418 -0
- package/dist/index.d.mts +2 -5897
- package/dist/index.d.ts +2 -5897
- package/dist/index.js +1334 -199
- package/dist/index.mjs +19 -1
- package/dist/noir-BTyLXLlZ.d.mts +467 -0
- package/dist/noir-BTyLXLlZ.d.ts +467 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +11 -112
- package/dist/proofs/noir.mjs +10 -13
- package/package.json +3 -3
- package/src/browser.ts +23 -0
- package/src/index.ts +32 -0
- package/src/proofs/browser-utils.ts +389 -0
- package/src/proofs/browser.ts +246 -19
- package/src/proofs/circuits/funding_proof.json +1 -1
- package/src/proofs/noir.ts +14 -14
- package/src/proofs/worker.ts +426 -0
- package/src/settlement/README.md +439 -0
- package/src/settlement/backends/direct-chain.ts +569 -0
- package/src/settlement/backends/index.ts +22 -0
- package/src/settlement/backends/near-intents.ts +480 -0
- package/src/settlement/backends/zcash-native.ts +516 -0
- package/src/settlement/index.ts +47 -0
- package/src/settlement/interface.ts +397 -0
- package/src/settlement/registry.ts +269 -0
- package/src/settlement/router.ts +383 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settlement Backend Interface for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* Defines a pluggable settlement layer abstraction for executing cross-chain swaps.
|
|
5
|
+
* Implementations can use NEAR Intents, Zcash, THORChain, or direct on-chain execution.
|
|
6
|
+
*
|
|
7
|
+
* @module settlement/interface
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ChainId,
|
|
12
|
+
PrivacyLevel,
|
|
13
|
+
HexString,
|
|
14
|
+
StealthMetaAddress,
|
|
15
|
+
Asset,
|
|
16
|
+
} from '@sip-protocol/types'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Settlement backend name type
|
|
20
|
+
*/
|
|
21
|
+
export type SettlementBackendName =
|
|
22
|
+
| 'near-intents'
|
|
23
|
+
| 'zcash'
|
|
24
|
+
| 'thorchain'
|
|
25
|
+
| 'direct-chain'
|
|
26
|
+
| string // Allow custom backends
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Swap status enum
|
|
30
|
+
*/
|
|
31
|
+
export enum SwapStatus {
|
|
32
|
+
/** Quote generated, awaiting deposit */
|
|
33
|
+
PENDING_DEPOSIT = 'pending_deposit',
|
|
34
|
+
/** Deposit confirmed, awaiting execution */
|
|
35
|
+
DEPOSIT_CONFIRMED = 'deposit_confirmed',
|
|
36
|
+
/** Swap in progress */
|
|
37
|
+
IN_PROGRESS = 'in_progress',
|
|
38
|
+
/** Swap completed successfully */
|
|
39
|
+
SUCCESS = 'success',
|
|
40
|
+
/** Swap failed */
|
|
41
|
+
FAILED = 'failed',
|
|
42
|
+
/** Swap cancelled or expired */
|
|
43
|
+
CANCELLED = 'cancelled',
|
|
44
|
+
/** Refund initiated */
|
|
45
|
+
REFUNDING = 'refunding',
|
|
46
|
+
/** Refund completed */
|
|
47
|
+
REFUNDED = 'refunded',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parameters for requesting a quote
|
|
52
|
+
*/
|
|
53
|
+
export interface QuoteParams {
|
|
54
|
+
/** Source chain */
|
|
55
|
+
fromChain: ChainId
|
|
56
|
+
/** Destination chain */
|
|
57
|
+
toChain: ChainId
|
|
58
|
+
/** Source token symbol */
|
|
59
|
+
fromToken: string
|
|
60
|
+
/** Destination token symbol */
|
|
61
|
+
toToken: string
|
|
62
|
+
/** Amount to swap (in smallest units) */
|
|
63
|
+
amount: bigint
|
|
64
|
+
/** Privacy level */
|
|
65
|
+
privacyLevel: PrivacyLevel
|
|
66
|
+
/** Recipient's stealth meta-address (for shielded/compliant modes) */
|
|
67
|
+
recipientMetaAddress?: StealthMetaAddress | string
|
|
68
|
+
/** Sender's address for refunds (for transparent mode or refunds) */
|
|
69
|
+
senderAddress?: string
|
|
70
|
+
/** Maximum acceptable slippage in basis points (100 = 1%) */
|
|
71
|
+
slippageTolerance?: number
|
|
72
|
+
/** Deadline for swap expiry (Unix timestamp in seconds) */
|
|
73
|
+
deadline?: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Quote response
|
|
78
|
+
*/
|
|
79
|
+
export interface Quote {
|
|
80
|
+
/** Unique quote identifier */
|
|
81
|
+
quoteId: string
|
|
82
|
+
/** Expected input amount (in smallest units) */
|
|
83
|
+
amountIn: string
|
|
84
|
+
/** Expected output amount (in smallest units) */
|
|
85
|
+
amountOut: string
|
|
86
|
+
/** Minimum output amount after slippage (in smallest units) */
|
|
87
|
+
minAmountOut: string
|
|
88
|
+
/** Estimated price impact in basis points */
|
|
89
|
+
priceImpact?: number
|
|
90
|
+
/** Estimated fees (network + protocol) */
|
|
91
|
+
fees: {
|
|
92
|
+
/** Network gas fees (in native token) */
|
|
93
|
+
networkFee: string
|
|
94
|
+
/** Protocol/solver fees (in output token) */
|
|
95
|
+
protocolFee: string
|
|
96
|
+
/** Total fees in USD (optional) */
|
|
97
|
+
totalFeeUSD?: string
|
|
98
|
+
}
|
|
99
|
+
/** Deposit address for input tokens */
|
|
100
|
+
depositAddress: string
|
|
101
|
+
/** Recipient address (stealth address if privacy mode) */
|
|
102
|
+
recipientAddress: string
|
|
103
|
+
/** Refund address (if deposit fails or expires) */
|
|
104
|
+
refundAddress?: string
|
|
105
|
+
/** Quote expiration timestamp */
|
|
106
|
+
expiresAt: number
|
|
107
|
+
/** Expected execution time in seconds */
|
|
108
|
+
estimatedTime?: number
|
|
109
|
+
/** Swap route details (optional, for transparency) */
|
|
110
|
+
route?: SwapRoute
|
|
111
|
+
/** Backend-specific metadata */
|
|
112
|
+
metadata?: Record<string, unknown>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Swap route details
|
|
117
|
+
*/
|
|
118
|
+
export interface SwapRoute {
|
|
119
|
+
/** Route steps */
|
|
120
|
+
steps: SwapRouteStep[]
|
|
121
|
+
/** Total number of hops */
|
|
122
|
+
hops: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Individual swap route step
|
|
127
|
+
*/
|
|
128
|
+
export interface SwapRouteStep {
|
|
129
|
+
/** Protocol/DEX name */
|
|
130
|
+
protocol: string
|
|
131
|
+
/** Input token */
|
|
132
|
+
tokenIn: Asset
|
|
133
|
+
/** Output token */
|
|
134
|
+
tokenOut: Asset
|
|
135
|
+
/** Pool/pair identifier */
|
|
136
|
+
poolId?: string
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Parameters for executing a swap
|
|
141
|
+
*/
|
|
142
|
+
export interface SwapParams {
|
|
143
|
+
/** Quote identifier from getQuote() */
|
|
144
|
+
quoteId: string
|
|
145
|
+
/** Transaction hash of deposit (if already deposited) */
|
|
146
|
+
depositTxHash?: string
|
|
147
|
+
/** NEAR account (if depositing from NEAR) */
|
|
148
|
+
nearAccount?: string
|
|
149
|
+
/** Additional backend-specific parameters */
|
|
150
|
+
metadata?: Record<string, unknown>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Swap execution result
|
|
155
|
+
*/
|
|
156
|
+
export interface SwapResult {
|
|
157
|
+
/** Unique swap identifier */
|
|
158
|
+
swapId: string
|
|
159
|
+
/** Current status */
|
|
160
|
+
status: SwapStatus
|
|
161
|
+
/** Quote ID */
|
|
162
|
+
quoteId: string
|
|
163
|
+
/** Deposit address */
|
|
164
|
+
depositAddress: string
|
|
165
|
+
/** Deposit transaction hash (after deposit) */
|
|
166
|
+
depositTxHash?: string
|
|
167
|
+
/** Settlement transaction hash (after completion) */
|
|
168
|
+
settlementTxHash?: string
|
|
169
|
+
/** Refund transaction hash (if refunded) */
|
|
170
|
+
refundTxHash?: string
|
|
171
|
+
/** Actual output amount (after completion) */
|
|
172
|
+
actualAmountOut?: string
|
|
173
|
+
/** Error message (if failed) */
|
|
174
|
+
errorMessage?: string
|
|
175
|
+
/** Backend-specific metadata */
|
|
176
|
+
metadata?: Record<string, unknown>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Swap status response
|
|
181
|
+
*/
|
|
182
|
+
export interface SwapStatusResponse {
|
|
183
|
+
/** Unique swap identifier */
|
|
184
|
+
swapId: string
|
|
185
|
+
/** Current status */
|
|
186
|
+
status: SwapStatus
|
|
187
|
+
/** Quote ID */
|
|
188
|
+
quoteId: string
|
|
189
|
+
/** Deposit address */
|
|
190
|
+
depositAddress: string
|
|
191
|
+
/** Expected input amount */
|
|
192
|
+
amountIn: string
|
|
193
|
+
/** Expected output amount */
|
|
194
|
+
amountOut: string
|
|
195
|
+
/** Deposit transaction hash (after deposit) */
|
|
196
|
+
depositTxHash?: string
|
|
197
|
+
/** Settlement transaction hash (after completion) */
|
|
198
|
+
settlementTxHash?: string
|
|
199
|
+
/** Refund transaction hash (if refunded) */
|
|
200
|
+
refundTxHash?: string
|
|
201
|
+
/** Actual output amount (after completion) */
|
|
202
|
+
actualAmountOut?: string
|
|
203
|
+
/** Error message (if failed) */
|
|
204
|
+
errorMessage?: string
|
|
205
|
+
/** Stealth address for recipient (if privacy mode) */
|
|
206
|
+
stealthRecipient?: string
|
|
207
|
+
/** Ephemeral public key (for recipient to derive stealth key) */
|
|
208
|
+
ephemeralPublicKey?: string
|
|
209
|
+
/** Last updated timestamp */
|
|
210
|
+
updatedAt: number
|
|
211
|
+
/** Backend-specific metadata */
|
|
212
|
+
metadata?: Record<string, unknown>
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Backend capabilities descriptor
|
|
217
|
+
*/
|
|
218
|
+
export interface BackendCapabilities {
|
|
219
|
+
/** Supported source chains */
|
|
220
|
+
supportedSourceChains: ChainId[]
|
|
221
|
+
/** Supported destination chains */
|
|
222
|
+
supportedDestinationChains: ChainId[]
|
|
223
|
+
/** Supported privacy levels */
|
|
224
|
+
supportedPrivacyLevels: PrivacyLevel[]
|
|
225
|
+
/** Maximum swap amount (in USD, undefined = no limit) */
|
|
226
|
+
maxSwapAmountUSD?: number
|
|
227
|
+
/** Minimum swap amount (in USD, undefined = no limit) */
|
|
228
|
+
minSwapAmountUSD?: number
|
|
229
|
+
/** Supports cancellation */
|
|
230
|
+
supportsCancellation: boolean
|
|
231
|
+
/** Supports refunds */
|
|
232
|
+
supportsRefunds: boolean
|
|
233
|
+
/** Average execution time in seconds */
|
|
234
|
+
averageExecutionTime?: number
|
|
235
|
+
/** Additional backend-specific capabilities */
|
|
236
|
+
features?: string[]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Settlement Backend Interface
|
|
241
|
+
*
|
|
242
|
+
* All settlement backends must implement this interface.
|
|
243
|
+
* This allows SIP to support multiple settlement layers (NEAR Intents, Zcash, THORChain, etc.)
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* class MySettlementBackend implements SettlementBackend {
|
|
248
|
+
* name = 'my-backend'
|
|
249
|
+
* supportedChains = ['ethereum', 'solana']
|
|
250
|
+
*
|
|
251
|
+
* async getQuote(params: QuoteParams): Promise<Quote> {
|
|
252
|
+
* // Implementation
|
|
253
|
+
* }
|
|
254
|
+
*
|
|
255
|
+
* async executeSwap(params: SwapParams): Promise<SwapResult> {
|
|
256
|
+
* // Implementation
|
|
257
|
+
* }
|
|
258
|
+
*
|
|
259
|
+
* async getStatus(swapId: string): Promise<SwapStatusResponse> {
|
|
260
|
+
* // Implementation
|
|
261
|
+
* }
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export interface SettlementBackend {
|
|
266
|
+
/**
|
|
267
|
+
* Backend name (e.g., 'near-intents', 'zcash', 'thorchain')
|
|
268
|
+
*/
|
|
269
|
+
readonly name: SettlementBackendName
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Backend capabilities
|
|
273
|
+
*/
|
|
274
|
+
readonly capabilities: BackendCapabilities
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get a quote for a cross-chain swap
|
|
278
|
+
*
|
|
279
|
+
* @param params - Quote parameters
|
|
280
|
+
* @returns Quote with pricing, fees, and deposit address
|
|
281
|
+
* @throws {ValidationError} If parameters are invalid
|
|
282
|
+
* @throws {NetworkError} If backend API is unavailable
|
|
283
|
+
*/
|
|
284
|
+
getQuote(params: QuoteParams): Promise<Quote>
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Execute a swap using a quote
|
|
288
|
+
*
|
|
289
|
+
* For most backends, this returns a deposit address and waits for user deposit.
|
|
290
|
+
* Some backends may require additional signing or approval.
|
|
291
|
+
*
|
|
292
|
+
* @param params - Swap execution parameters
|
|
293
|
+
* @returns Swap result with status and transaction details
|
|
294
|
+
* @throws {ValidationError} If quote is invalid or expired
|
|
295
|
+
* @throws {NetworkError} If backend API is unavailable
|
|
296
|
+
*/
|
|
297
|
+
executeSwap(params: SwapParams): Promise<SwapResult>
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get current swap status
|
|
301
|
+
*
|
|
302
|
+
* @param swapId - Swap identifier (typically the deposit address)
|
|
303
|
+
* @returns Current swap status
|
|
304
|
+
* @throws {ValidationError} If swap ID is invalid
|
|
305
|
+
* @throws {NetworkError} If backend API is unavailable
|
|
306
|
+
*/
|
|
307
|
+
getStatus(swapId: string): Promise<SwapStatusResponse>
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Cancel a pending swap (optional)
|
|
311
|
+
*
|
|
312
|
+
* Only supported by backends with cancellation capabilities.
|
|
313
|
+
* Check `capabilities.supportsCancellation` before calling.
|
|
314
|
+
*
|
|
315
|
+
* @param swapId - Swap identifier to cancel
|
|
316
|
+
* @throws {ValidationError} If swap cannot be cancelled (already executed, etc.)
|
|
317
|
+
* @throws {NetworkError} If backend API is unavailable
|
|
318
|
+
* @throws {ProofError} If cancellation is not supported
|
|
319
|
+
*/
|
|
320
|
+
cancel?(swapId: string): Promise<void>
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Wait for swap completion (optional)
|
|
324
|
+
*
|
|
325
|
+
* Polls swap status until completion or timeout.
|
|
326
|
+
* Backends may provide more efficient implementations (webhooks, subscriptions).
|
|
327
|
+
*
|
|
328
|
+
* @param swapId - Swap identifier to monitor
|
|
329
|
+
* @param options - Polling options
|
|
330
|
+
* @returns Final swap status
|
|
331
|
+
* @throws {ValidationError} If swap ID is invalid
|
|
332
|
+
* @throws {NetworkError} If backend API is unavailable
|
|
333
|
+
*/
|
|
334
|
+
waitForCompletion?(
|
|
335
|
+
swapId: string,
|
|
336
|
+
options?: {
|
|
337
|
+
/** Polling interval in milliseconds (default: 5000) */
|
|
338
|
+
interval?: number
|
|
339
|
+
/** Maximum wait time in milliseconds (default: 600000 = 10 minutes) */
|
|
340
|
+
timeout?: number
|
|
341
|
+
/** Status change callback */
|
|
342
|
+
onStatusChange?: (status: SwapStatusResponse) => void
|
|
343
|
+
}
|
|
344
|
+
): Promise<SwapStatusResponse>
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get a dry quote (preview without creating deposit address)
|
|
348
|
+
*
|
|
349
|
+
* Useful for showing estimates without committing to a swap.
|
|
350
|
+
* Not all backends support this (return same as getQuote if not).
|
|
351
|
+
*
|
|
352
|
+
* @param params - Quote parameters
|
|
353
|
+
* @returns Quote preview
|
|
354
|
+
*/
|
|
355
|
+
getDryQuote?(params: QuoteParams): Promise<Quote>
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Notify backend of deposit transaction (optional)
|
|
359
|
+
*
|
|
360
|
+
* Some backends require explicit notification after user deposits.
|
|
361
|
+
* Check backend documentation for requirements.
|
|
362
|
+
*
|
|
363
|
+
* @param swapId - Swap identifier (typically deposit address)
|
|
364
|
+
* @param txHash - Deposit transaction hash
|
|
365
|
+
* @param metadata - Additional backend-specific data
|
|
366
|
+
*/
|
|
367
|
+
notifyDeposit?(
|
|
368
|
+
swapId: string,
|
|
369
|
+
txHash: string,
|
|
370
|
+
metadata?: Record<string, unknown>
|
|
371
|
+
): Promise<void>
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Settlement backend factory function type
|
|
376
|
+
*/
|
|
377
|
+
export type SettlementBackendFactory<TConfig = unknown> = (
|
|
378
|
+
config: TConfig
|
|
379
|
+
) => SettlementBackend
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Settlement backend registry entry
|
|
383
|
+
*/
|
|
384
|
+
export interface SettlementBackendRegistry {
|
|
385
|
+
/** Backend name */
|
|
386
|
+
name: SettlementBackendName
|
|
387
|
+
/** Factory function */
|
|
388
|
+
factory: SettlementBackendFactory
|
|
389
|
+
/** Human-readable display name */
|
|
390
|
+
displayName: string
|
|
391
|
+
/** Description */
|
|
392
|
+
description: string
|
|
393
|
+
/** Homepage URL */
|
|
394
|
+
homepage?: string
|
|
395
|
+
/** Documentation URL */
|
|
396
|
+
docs?: string
|
|
397
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settlement Backend Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages multiple settlement backends and provides route-based selection.
|
|
5
|
+
*
|
|
6
|
+
* @module settlement/registry
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ChainId } from '@sip-protocol/types'
|
|
10
|
+
import type { SettlementBackend } from './interface'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Route identifier
|
|
14
|
+
*/
|
|
15
|
+
export interface Route {
|
|
16
|
+
/** Source chain */
|
|
17
|
+
fromChain: ChainId
|
|
18
|
+
/** Destination chain */
|
|
19
|
+
toChain: ChainId
|
|
20
|
+
/** Backend name that supports this route */
|
|
21
|
+
backend: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Settlement Registry Error
|
|
26
|
+
*/
|
|
27
|
+
export class SettlementRegistryError extends Error {
|
|
28
|
+
constructor(message: string) {
|
|
29
|
+
super(message)
|
|
30
|
+
this.name = 'SettlementRegistryError'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Settlement Backend Registry
|
|
36
|
+
*
|
|
37
|
+
* Manages multiple settlement backends and provides route-based selection.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const registry = new SettlementRegistry()
|
|
42
|
+
*
|
|
43
|
+
* // Register backends
|
|
44
|
+
* registry.register(nearIntentsBackend)
|
|
45
|
+
* registry.register(zcashBackend)
|
|
46
|
+
*
|
|
47
|
+
* // Get backend by name
|
|
48
|
+
* const backend = registry.get('near-intents')
|
|
49
|
+
*
|
|
50
|
+
* // Find best backend for a route
|
|
51
|
+
* const bestBackend = registry.getBestForRoute('ethereum', 'solana')
|
|
52
|
+
*
|
|
53
|
+
* // List all supported routes
|
|
54
|
+
* const routes = registry.getSupportedRoutes()
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export class SettlementRegistry {
|
|
58
|
+
private backends: Map<string, SettlementBackend> = new Map()
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Register a settlement backend
|
|
62
|
+
*
|
|
63
|
+
* @param backend - Settlement backend to register
|
|
64
|
+
* @throws {SettlementRegistryError} If backend with same name already exists
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* registry.register(nearIntentsBackend)
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
register(backend: SettlementBackend): void {
|
|
72
|
+
if (this.backends.has(backend.name)) {
|
|
73
|
+
throw new SettlementRegistryError(
|
|
74
|
+
`Backend '${backend.name}' is already registered`
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.backends.set(backend.name, backend)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get a settlement backend by name
|
|
83
|
+
*
|
|
84
|
+
* @param name - Backend name
|
|
85
|
+
* @returns Settlement backend
|
|
86
|
+
* @throws {SettlementRegistryError} If backend is not found
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const backend = registry.get('near-intents')
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
get(name: string): SettlementBackend {
|
|
94
|
+
const backend = this.backends.get(name)
|
|
95
|
+
if (!backend) {
|
|
96
|
+
throw new SettlementRegistryError(`Backend '${name}' not found`)
|
|
97
|
+
}
|
|
98
|
+
return backend
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* List all registered backend names
|
|
103
|
+
*
|
|
104
|
+
* @returns Array of backend names
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const names = registry.list()
|
|
109
|
+
* // ['near-intents', 'zcash', 'thorchain']
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
list(): string[] {
|
|
113
|
+
return Array.from(this.backends.keys())
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get the best backend for a specific route
|
|
118
|
+
*
|
|
119
|
+
* Selection criteria (in order of priority):
|
|
120
|
+
* 1. Backends that support both source and destination chains
|
|
121
|
+
* 2. Backends with faster average execution time
|
|
122
|
+
* 3. First registered backend (if no execution time info)
|
|
123
|
+
*
|
|
124
|
+
* @param fromChain - Source chain
|
|
125
|
+
* @param toChain - Destination chain
|
|
126
|
+
* @returns Best settlement backend for the route
|
|
127
|
+
* @throws {SettlementRegistryError} If no backend supports the route
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const backend = registry.getBestForRoute('ethereum', 'solana')
|
|
132
|
+
* const quote = await backend.getQuote({ ... })
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
getBestForRoute(fromChain: ChainId, toChain: ChainId): SettlementBackend {
|
|
136
|
+
const supportedBackends: SettlementBackend[] = []
|
|
137
|
+
|
|
138
|
+
// Find all backends that support this route
|
|
139
|
+
for (const backend of Array.from(this.backends.values())) {
|
|
140
|
+
const { supportedSourceChains, supportedDestinationChains } =
|
|
141
|
+
backend.capabilities
|
|
142
|
+
|
|
143
|
+
const supportsSource = supportedSourceChains.includes(fromChain)
|
|
144
|
+
const supportsDestination =
|
|
145
|
+
supportedDestinationChains.includes(toChain)
|
|
146
|
+
|
|
147
|
+
if (supportsSource && supportsDestination) {
|
|
148
|
+
supportedBackends.push(backend)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (supportedBackends.length === 0) {
|
|
153
|
+
throw new SettlementRegistryError(
|
|
154
|
+
`No backend supports route from '${fromChain}' to '${toChain}'`
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If only one backend supports the route, return it
|
|
159
|
+
if (supportedBackends.length === 1) {
|
|
160
|
+
return supportedBackends[0]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Sort by average execution time (fastest first)
|
|
164
|
+
// Backends without execution time info go last
|
|
165
|
+
supportedBackends.sort((a, b) => {
|
|
166
|
+
const timeA = a.capabilities.averageExecutionTime ?? Infinity
|
|
167
|
+
const timeB = b.capabilities.averageExecutionTime ?? Infinity
|
|
168
|
+
return timeA - timeB
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
return supportedBackends[0]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get all supported routes across all registered backends
|
|
176
|
+
*
|
|
177
|
+
* @returns Array of supported routes
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const routes = registry.getSupportedRoutes()
|
|
182
|
+
* // [
|
|
183
|
+
* // { fromChain: 'ethereum', toChain: 'solana', backend: 'near-intents' },
|
|
184
|
+
* // { fromChain: 'solana', toChain: 'ethereum', backend: 'near-intents' },
|
|
185
|
+
* // { fromChain: 'ethereum', toChain: 'zcash', backend: 'zcash' },
|
|
186
|
+
* // ...
|
|
187
|
+
* // ]
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
getSupportedRoutes(): Route[] {
|
|
191
|
+
const routes: Route[] = []
|
|
192
|
+
|
|
193
|
+
for (const backend of Array.from(this.backends.values())) {
|
|
194
|
+
const { supportedSourceChains, supportedDestinationChains } =
|
|
195
|
+
backend.capabilities
|
|
196
|
+
|
|
197
|
+
// Generate all possible routes for this backend
|
|
198
|
+
for (const fromChain of supportedSourceChains) {
|
|
199
|
+
for (const toChain of supportedDestinationChains) {
|
|
200
|
+
routes.push({
|
|
201
|
+
fromChain,
|
|
202
|
+
toChain,
|
|
203
|
+
backend: backend.name,
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return routes
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if a backend is registered
|
|
214
|
+
*
|
|
215
|
+
* @param name - Backend name
|
|
216
|
+
* @returns True if backend is registered
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* if (registry.has('near-intents')) {
|
|
221
|
+
* const backend = registry.get('near-intents')
|
|
222
|
+
* }
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
has(name: string): boolean {
|
|
226
|
+
return this.backends.has(name)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Unregister a settlement backend
|
|
231
|
+
*
|
|
232
|
+
* @param name - Backend name to unregister
|
|
233
|
+
* @returns True if backend was unregistered, false if not found
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* registry.unregister('near-intents')
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
unregister(name: string): boolean {
|
|
241
|
+
return this.backends.delete(name)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Clear all registered backends
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* registry.clear()
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
clear(): void {
|
|
253
|
+
this.backends.clear()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get number of registered backends
|
|
258
|
+
*
|
|
259
|
+
* @returns Number of registered backends
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* const count = registry.size()
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
size(): number {
|
|
267
|
+
return this.backends.size
|
|
268
|
+
}
|
|
269
|
+
}
|