@sip-protocol/sdk 0.2.10 → 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 +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1334 -199
- package/dist/browser.mjs +19 -1
- package/dist/chunk-4IFOPYJF.mjs +11950 -0
- package/dist/chunk-4VJHI66K.mjs +12120 -0
- package/dist/chunk-5BAS4D44.mjs +10283 -0
- package/dist/chunk-6WOV2YNG.mjs +10179 -0
- package/dist/chunk-7IMRM7LN.mjs +12149 -0
- package/dist/chunk-DU7LQDD2.mjs +10148 -0
- package/dist/{chunk-AV37IZST.mjs → chunk-JNNXNTSS.mjs} +14 -0
- package/dist/chunk-KXN6IWL5.mjs +10736 -0
- package/dist/chunk-MR7HRCRS.mjs +10165 -0
- package/dist/chunk-NDGUWOOZ.mjs +10157 -0
- package/dist/chunk-O4Y2ZUDL.mjs +12721 -0
- package/dist/chunk-UPTISVCY.mjs +10304 -0
- package/dist/chunk-VITVG25F.mjs +982 -0
- package/dist/chunk-VXSHK7US.mjs +10158 -0
- package/dist/chunk-W3YXIQ7L.mjs +11950 -0
- package/dist/index-Ba7njCU3.d.ts +6925 -0
- package/dist/index-Co26-vbG.d.mts +6925 -0
- package/dist/{index-CAhjA4kh.d.mts → index-DqZoHYKI.d.mts} +362 -6
- package/dist/index-dTtK_DTl.d.ts +6762 -0
- package/dist/index-jnkYu-Z4.d.mts +6762 -0
- package/dist/{index-BFOKTz2z.d.ts → index-vB1N1mHd.d.ts} +362 -6
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1334 -199
- package/dist/index.mjs +19 -1
- package/dist/noir-BHQtFvRk.d.mts +467 -0
- package/dist/noir-BHQtFvRk.d.ts +467 -0
- package/package.json +14 -14
- package/src/index.ts +32 -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
- package/LICENSE +0 -21
|
@@ -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/)
|