@sip-protocol/sdk 0.5.0 → 0.6.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/src/sip.ts CHANGED
@@ -32,50 +32,145 @@ import { isValidChainId } from './validation'
32
32
  import { NEARIntentsAdapter, type NEARIntentsAdapterConfig, type SwapRequest } from './adapters'
33
33
 
34
34
  /**
35
- * SIP SDK configuration
35
+ * Configuration options for the SIP SDK client
36
+ *
37
+ * Controls network selection, privacy defaults, proof generation, and settlement backend.
38
+ *
39
+ * @example Basic configuration
40
+ * ```typescript
41
+ * const sip = new SIP({
42
+ * network: 'testnet',
43
+ * defaultPrivacy: PrivacyLevel.SHIELDED
44
+ * })
45
+ * ```
46
+ *
47
+ * @example Production configuration with NEAR Intents
48
+ * ```typescript
49
+ * const sip = new SIP({
50
+ * network: 'mainnet',
51
+ * mode: 'production',
52
+ * proofProvider: new MockProofProvider(),
53
+ * intentsAdapter: {
54
+ * jwtToken: process.env.NEAR_INTENTS_JWT
55
+ * }
56
+ * })
57
+ * ```
36
58
  */
37
59
  export interface SIPConfig {
38
- /** Network: mainnet or testnet */
60
+ /**
61
+ * Network to operate on
62
+ *
63
+ * - `'mainnet'`: Production network with real assets
64
+ * - `'testnet'`: Test network for development
65
+ */
39
66
  network: 'mainnet' | 'testnet'
67
+
40
68
  /**
41
- * Mode: 'demo' for mock data, 'production' for real NEAR Intents
69
+ * Operating mode for quote fetching and execution
70
+ *
71
+ * - `'demo'`: Returns mock quotes for testing (default)
72
+ * - `'production'`: Uses real NEAR Intents 1Click API
73
+ *
42
74
  * @default 'demo'
43
75
  */
44
76
  mode?: 'demo' | 'production'
45
- /** Default privacy level */
77
+
78
+ /**
79
+ * Default privacy level for new intents
80
+ *
81
+ * Can be overridden per intent. See {@link PrivacyLevel} for options.
82
+ *
83
+ * @default PrivacyLevel.SHIELDED
84
+ */
46
85
  defaultPrivacy?: PrivacyLevel
47
- /** RPC endpoints for chains */
48
- rpcEndpoints?: Partial<Record<ChainId, string>>
86
+
49
87
  /**
50
- * Proof provider for ZK proof generation
88
+ * Custom RPC endpoints for blockchain connections
51
89
  *
52
- * If not provided, proof generation will not be available.
53
- * Use MockProofProvider for testing, NoirProofProvider for production.
90
+ * Maps chain IDs to RPC URLs. Used for direct blockchain interactions
91
+ * when not using settlement adapters.
54
92
  *
55
93
  * @example
56
94
  * ```typescript
95
+ * {
96
+ * rpcEndpoints: {
97
+ * ethereum: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
98
+ * solana: 'https://api.mainnet-beta.solana.com'
99
+ * }
100
+ * }
101
+ * ```
102
+ */
103
+ rpcEndpoints?: Partial<Record<ChainId, string>>
104
+
105
+ /**
106
+ * Proof provider for zero-knowledge proof generation
107
+ *
108
+ * Required for SHIELDED and COMPLIANT privacy modes. If not provided,
109
+ * intents will be created without proofs (must be attached later).
110
+ *
111
+ * **Available providers:**
112
+ * - {@link MockProofProvider}: For testing and development
113
+ * - `NoirProofProvider`: For production (import from '@sip-protocol/sdk/proofs/noir')
114
+ * - `BrowserNoirProvider`: For browser environments (import from '@sip-protocol/sdk/browser')
115
+ *
116
+ * @example Testing with mock proofs
117
+ * ```typescript
57
118
  * import { MockProofProvider } from '@sip-protocol/sdk'
58
119
  *
59
120
  * const sip = new SIP({
60
121
  * network: 'testnet',
61
- * proofProvider: new MockProofProvider(),
122
+ * proofProvider: new MockProofProvider()
123
+ * })
124
+ * ```
125
+ *
126
+ * @example Production with Noir proofs (Node.js)
127
+ * ```typescript
128
+ * import { NoirProofProvider } from '@sip-protocol/sdk/proofs/noir'
129
+ *
130
+ * const provider = new NoirProofProvider()
131
+ * await provider.initialize()
132
+ *
133
+ * const sip = new SIP({
134
+ * network: 'mainnet',
135
+ * proofProvider: provider
62
136
  * })
63
137
  * ```
64
138
  */
65
139
  proofProvider?: ProofProvider
140
+
66
141
  /**
67
- * NEAR Intents adapter configuration
142
+ * NEAR Intents adapter for production mode
68
143
  *
69
- * Required for production mode. Provides connection to 1Click API.
144
+ * Required when `mode: 'production'`. Provides connection to the 1Click API
145
+ * for real cross-chain swaps via NEAR Intents.
70
146
  *
71
- * @example
147
+ * Can be either:
148
+ * - Configuration object ({@link NEARIntentsAdapterConfig})
149
+ * - Pre-configured adapter instance ({@link NEARIntentsAdapter})
150
+ *
151
+ * @example With JWT token
72
152
  * ```typescript
73
153
  * const sip = new SIP({
74
154
  * network: 'mainnet',
75
155
  * mode: 'production',
76
156
  * intentsAdapter: {
77
- * jwtToken: process.env.NEAR_INTENTS_JWT,
78
- * },
157
+ * jwtToken: process.env.NEAR_INTENTS_JWT
158
+ * }
159
+ * })
160
+ * ```
161
+ *
162
+ * @example With pre-configured adapter
163
+ * ```typescript
164
+ * import { createNEARIntentsAdapter } from '@sip-protocol/sdk'
165
+ *
166
+ * const adapter = createNEARIntentsAdapter({
167
+ * jwtToken: process.env.NEAR_INTENTS_JWT
168
+ * })
169
+ *
170
+ * const sip = new SIP({
171
+ * network: 'mainnet',
172
+ * mode: 'production',
173
+ * intentsAdapter: adapter
79
174
  * })
80
175
  * ```
81
176
  */
@@ -83,33 +178,212 @@ export interface SIPConfig {
83
178
  }
84
179
 
85
180
  /**
86
- * Wallet adapter interface
181
+ * Wallet adapter interface for blockchain interactions
182
+ *
183
+ * Provides a unified interface for signing messages and transactions
184
+ * across different blockchain wallets (Ethereum, Solana, etc.).
185
+ *
186
+ * @example Implementing a wallet adapter
187
+ * ```typescript
188
+ * class MyWalletAdapter implements WalletAdapter {
189
+ * chain = 'ethereum' as const
190
+ * address = '0x...'
191
+ *
192
+ * async signMessage(message: string): Promise<string> {
193
+ * return await wallet.signMessage(message)
194
+ * }
195
+ *
196
+ * async signTransaction(tx: unknown): Promise<unknown> {
197
+ * return await wallet.signTransaction(tx)
198
+ * }
199
+ * }
200
+ * ```
87
201
  */
88
202
  export interface WalletAdapter {
89
- /** Connected chain */
203
+ /**
204
+ * Blockchain network this wallet is connected to
205
+ *
206
+ * Must match one of the supported {@link ChainId} values.
207
+ */
90
208
  chain: ChainId
91
- /** Wallet address */
209
+
210
+ /**
211
+ * Wallet address on the connected chain
212
+ *
213
+ * Format depends on the chain:
214
+ * - Ethereum: `0x...` (checksummed)
215
+ * - Solana: Base58-encoded public key
216
+ * - NEAR: Account ID or implicit address
217
+ */
92
218
  address: string
93
- /** Sign a message */
219
+
220
+ /**
221
+ * Sign a message with the wallet's private key
222
+ *
223
+ * @param message - UTF-8 string to sign
224
+ * @returns Signature as hex string or base58 (chain-dependent)
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * const signature = await wallet.signMessage('Hello SIP!')
229
+ * ```
230
+ */
94
231
  signMessage(message: string): Promise<string>
95
- /** Sign a transaction */
232
+
233
+ /**
234
+ * Sign a transaction without broadcasting
235
+ *
236
+ * @param tx - Chain-specific transaction object
237
+ * @returns Signed transaction ready for broadcast
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const signedTx = await wallet.signTransaction(tx)
242
+ * ```
243
+ */
96
244
  signTransaction(tx: unknown): Promise<unknown>
97
- /** Send a transaction (optional) */
245
+
246
+ /**
247
+ * Sign and broadcast a transaction (optional)
248
+ *
249
+ * If implemented, allows the wallet to handle both signing and sending.
250
+ * If not implemented, caller must broadcast the signed transaction separately.
251
+ *
252
+ * @param tx - Chain-specific transaction object
253
+ * @returns Transaction hash after broadcast
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * if (wallet.sendTransaction) {
258
+ * const txHash = await wallet.sendTransaction(tx)
259
+ * }
260
+ * ```
261
+ */
98
262
  sendTransaction?(tx: unknown): Promise<string>
99
263
  }
100
264
 
101
265
  /**
102
- * Extended quote with deposit info for production mode
266
+ * Extended quote with production-specific metadata
267
+ *
268
+ * In production mode, quotes include deposit addresses and raw API responses
269
+ * from the NEAR 1Click API for advanced use cases.
103
270
  */
104
271
  export interface ProductionQuote extends Quote {
105
- /** Deposit address for input tokens (production mode only) */
272
+ /**
273
+ * Deposit address for input tokens
274
+ *
275
+ * When using NEAR Intents in production mode, users deposit funds to this
276
+ * address to initiate the swap.
277
+ *
278
+ * Only present in production mode quotes.
279
+ */
106
280
  depositAddress?: string
107
- /** Raw 1Click quote response (production mode only) */
281
+
282
+ /**
283
+ * Raw response from NEAR 1Click API
284
+ *
285
+ * Contains the complete quote data from the settlement backend.
286
+ * Useful for debugging or accessing provider-specific metadata.
287
+ *
288
+ * Only present in production mode quotes.
289
+ */
108
290
  rawQuote?: OneClickQuoteResponse
109
291
  }
110
292
 
111
293
  /**
112
- * Main SIP SDK class
294
+ * Main SIP SDK client for privacy-preserving cross-chain transactions
295
+ *
296
+ * The SIP class is the primary interface for interacting with the Shielded Intents Protocol.
297
+ * It provides methods for:
298
+ *
299
+ * - Creating shielded intents with privacy guarantees
300
+ * - Fetching quotes from solvers/market makers
301
+ * - Executing cross-chain swaps via settlement backends
302
+ * - Managing stealth addresses and viewing keys
303
+ * - Generating zero-knowledge proofs
304
+ *
305
+ * **Key Concepts:**
306
+ *
307
+ * - **Intent**: A declaration of desired output (e.g., "I want 95+ ZEC on Zcash")
308
+ * - **Privacy Levels**: transparent (public), shielded (private), compliant (private + auditable)
309
+ * - **Stealth Address**: One-time recipient address for unlinkable transactions
310
+ * - **Commitment**: Cryptographic hiding of amounts using Pedersen commitments
311
+ * - **Viewing Key**: Selective disclosure key for compliance/auditing
312
+ *
313
+ * @example Basic usage (demo mode)
314
+ * ```typescript
315
+ * import { SIP, PrivacyLevel } from '@sip-protocol/sdk'
316
+ *
317
+ * // Initialize client
318
+ * const sip = new SIP({ network: 'testnet' })
319
+ *
320
+ * // Create a shielded intent
321
+ * const intent = await sip.createIntent({
322
+ * input: {
323
+ * asset: { chain: 'solana', symbol: 'SOL', address: null, decimals: 9 },
324
+ * amount: 10n * 10n**9n, // 10 SOL
325
+ * },
326
+ * output: {
327
+ * asset: { chain: 'ethereum', symbol: 'ETH', address: null, decimals: 18 },
328
+ * minAmount: 0n, // Accept any amount
329
+ * maxSlippage: 0.01, // 1%
330
+ * },
331
+ * privacy: PrivacyLevel.SHIELDED,
332
+ * })
333
+ *
334
+ * // Get quotes from solvers
335
+ * const quotes = await sip.getQuotes(intent)
336
+ *
337
+ * // Execute with best quote
338
+ * const result = await sip.execute(intent, quotes[0])
339
+ * console.log('Swap completed:', result.txHash)
340
+ * ```
341
+ *
342
+ * @example Production mode with NEAR Intents
343
+ * ```typescript
344
+ * import { SIP, PrivacyLevel, MockProofProvider } from '@sip-protocol/sdk'
345
+ *
346
+ * // Initialize with production backend
347
+ * const sip = new SIP({
348
+ * network: 'mainnet',
349
+ * mode: 'production',
350
+ * proofProvider: new MockProofProvider(), // Use NoirProofProvider in prod
351
+ * intentsAdapter: {
352
+ * jwtToken: process.env.NEAR_INTENTS_JWT,
353
+ * },
354
+ * })
355
+ *
356
+ * // Connect wallet
357
+ * sip.connect(myWalletAdapter)
358
+ *
359
+ * // Generate stealth keys for receiving
360
+ * const stealthMetaAddress = sip.generateStealthKeys('ethereum', 'My Privacy Wallet')
361
+ *
362
+ * // Create intent with stealth recipient
363
+ * const intent = await sip.createIntent({
364
+ * input: { asset: { chain: 'near', symbol: 'NEAR', address: null, decimals: 24 }, amount: 100n },
365
+ * output: { asset: { chain: 'zcash', symbol: 'ZEC', address: null, decimals: 8 }, minAmount: 0n, maxSlippage: 0.01 },
366
+ * privacy: PrivacyLevel.SHIELDED,
367
+ * recipientMetaAddress: sip.getStealthAddress(),
368
+ * })
369
+ *
370
+ * // Get real quotes
371
+ * const quotes = await sip.getQuotes(intent)
372
+ *
373
+ * // Execute with deposit callback
374
+ * const result = await sip.execute(intent, quotes[0], {
375
+ * onDepositRequired: async (depositAddr, amount) => {
376
+ * console.log(`Deposit ${amount} to ${depositAddr}`)
377
+ * const tx = await wallet.transfer(depositAddr, amount)
378
+ * return tx.hash
379
+ * },
380
+ * onStatusUpdate: (status) => console.log('Status:', status),
381
+ * })
382
+ * ```
383
+ *
384
+ * @see {@link SIPConfig} for configuration options
385
+ * @see {@link createShieldedIntent} for intent creation
386
+ * @see {@link PrivacyLevel} for privacy modes
113
387
  */
114
388
  export class SIP {
115
389
  private config: SIPConfig & { mode: 'demo' | 'production' }
@@ -124,6 +398,31 @@ export class SIP {
124
398
  /** Cache of pending swaps by intent ID */
125
399
  private pendingSwaps: Map<string, { depositAddress: string; quote: OneClickQuoteResponse }> = new Map()
126
400
 
401
+ /**
402
+ * Create a new SIP SDK client
403
+ *
404
+ * @param config - Configuration options for the client
405
+ * @throws {ValidationError} If configuration is invalid
406
+ *
407
+ * @example Minimal configuration
408
+ * ```typescript
409
+ * const sip = new SIP({ network: 'testnet' })
410
+ * ```
411
+ *
412
+ * @example Full configuration
413
+ * ```typescript
414
+ * const sip = new SIP({
415
+ * network: 'mainnet',
416
+ * mode: 'production',
417
+ * defaultPrivacy: PrivacyLevel.COMPLIANT,
418
+ * proofProvider: await NoirProofProvider.create(),
419
+ * intentsAdapter: { jwtToken: process.env.JWT },
420
+ * rpcEndpoints: {
421
+ * ethereum: 'https://eth-mainnet.g.alchemy.com/v2/KEY',
422
+ * },
423
+ * })
424
+ * ```
425
+ */
127
426
  constructor(config: SIPConfig) {
128
427
  // Validate config
129
428
  if (!config || typeof config !== 'object') {
package/src/stealth.ts CHANGED
@@ -36,12 +36,66 @@ import {
36
36
  import { secureWipe, secureWipeAll } from './secure-memory'
37
37
 
38
38
  /**
39
- * Generate a new stealth meta-address keypair
39
+ * Generate a new stealth meta-address keypair for receiving private payments
40
40
  *
41
- * @param chain - Target chain for the addresses
42
- * @param label - Optional human-readable label
43
- * @returns Stealth meta-address and private keys
44
- * @throws {ValidationError} If chain is invalid
41
+ * Creates a reusable meta-address that senders can use to derive one-time stealth
42
+ * addresses. The recipient publishes the meta-address publicly, and senders generate
43
+ * unique payment addresses from it.
44
+ *
45
+ * **Security:** Private keys must be stored securely and never shared. The meta-address
46
+ * (containing only public keys) can be safely published.
47
+ *
48
+ * **Algorithm:** Uses secp256k1 elliptic curve (EIP-5564 style) for:
49
+ * - Ethereum, Polygon, Arbitrum, Optimism, Base, Bitcoin, Zcash
50
+ *
51
+ * For Solana/NEAR/Aptos/Sui chains, use {@link generateEd25519StealthMetaAddress} instead.
52
+ *
53
+ * @param chain - Target blockchain network (determines address format)
54
+ * @param label - Optional human-readable label for identification
55
+ * @returns Object containing:
56
+ * - `metaAddress`: Public keys to share with senders
57
+ * - `spendingPrivateKey`: Secret key for claiming funds (keep secure!)
58
+ * - `viewingPrivateKey`: Secret key for scanning incoming payments (keep secure!)
59
+ *
60
+ * @throws {ValidationError} If chain is invalid or not supported
61
+ *
62
+ * @example Generate stealth keys for Ethereum
63
+ * ```typescript
64
+ * import { generateStealthMetaAddress, encodeStealthMetaAddress } from '@sip-protocol/sdk'
65
+ *
66
+ * // Generate keys
67
+ * const { metaAddress, spendingPrivateKey, viewingPrivateKey } =
68
+ * generateStealthMetaAddress('ethereum', 'My Privacy Wallet')
69
+ *
70
+ * // Encode for sharing (QR code, website, etc.)
71
+ * const encoded = encodeStealthMetaAddress(metaAddress)
72
+ * console.log('Share this:', encoded)
73
+ * // Output: "sip:ethereum:0x02abc...123:0x03def...456"
74
+ *
75
+ * // Store private keys securely (e.g., encrypted keystore)
76
+ * secureStorage.save({
77
+ * spendingPrivateKey,
78
+ * viewingPrivateKey,
79
+ * })
80
+ * ```
81
+ *
82
+ * @example Multi-chain setup
83
+ * ```typescript
84
+ * // Generate different stealth keys for each chain
85
+ * const ethKeys = generateStealthMetaAddress('ethereum', 'ETH Privacy')
86
+ * const zkKeys = generateStealthMetaAddress('zcash', 'ZEC Privacy')
87
+ *
88
+ * // Publish meta-addresses
89
+ * publishToProfile({
90
+ * ethereum: encodeStealthMetaAddress(ethKeys.metaAddress),
91
+ * zcash: encodeStealthMetaAddress(zkKeys.metaAddress),
92
+ * })
93
+ * ```
94
+ *
95
+ * @see {@link generateStealthAddress} to generate payment addresses as a sender
96
+ * @see {@link encodeStealthMetaAddress} to encode for sharing
97
+ * @see {@link deriveStealthPrivateKey} to claim funds as a recipient
98
+ * @see {@link generateEd25519StealthMetaAddress} for Solana/NEAR chains
45
99
  */
46
100
  export function generateStealthMetaAddress(
47
101
  chain: ChainId,
@@ -125,11 +179,68 @@ function validateStealthMetaAddress(
125
179
  }
126
180
 
127
181
  /**
128
- * Generate a one-time stealth address for a recipient
182
+ * Generate a one-time stealth address for sending funds to a recipient
129
183
  *
130
- * @param recipientMetaAddress - Recipient's published stealth meta-address
131
- * @returns Stealth address data (address + ephemeral key for publication)
132
- * @throws {ValidationError} If recipientMetaAddress is invalid
184
+ * As a sender, use this function to create a unique, unlinkable payment address
185
+ * from the recipient's public meta-address. Each call generates a new address
186
+ * that only the recipient can link to their identity.
187
+ *
188
+ * **Privacy Properties:**
189
+ * - Address is unique per transaction (prevents on-chain linkability)
190
+ * - Only recipient can detect and claim payments
191
+ * - Third-party observers cannot link payments to the same recipient
192
+ * - View tag enables efficient payment scanning
193
+ *
194
+ * **Algorithm (EIP-5564 DKSAP):**
195
+ * 1. Generate ephemeral keypair (r, R = r*G)
196
+ * 2. Compute shared secret: S = r * P_spend
197
+ * 3. Derive stealth address: A = P_view + hash(S)*G
198
+ * 4. Publish (R, A) on-chain; keep r secret
199
+ *
200
+ * @param recipientMetaAddress - Recipient's public stealth meta-address
201
+ * @returns Object containing:
202
+ * - `stealthAddress`: One-time payment address to publish on-chain
203
+ * - `sharedSecret`: Secret for sender's records (optional, don't publish!)
204
+ *
205
+ * @throws {ValidationError} If meta-address is invalid or malformed
206
+ *
207
+ * @example Send shielded payment
208
+ * ```typescript
209
+ * import { generateStealthAddress, decodeStealthMetaAddress } from '@sip-protocol/sdk'
210
+ *
211
+ * // Recipient shares their meta-address (e.g., on website, profile)
212
+ * const recipientMetaAddr = 'sip:ethereum:0x02abc...123:0x03def...456'
213
+ *
214
+ * // Decode the meta-address
215
+ * const metaAddress = decodeStealthMetaAddress(recipientMetaAddr)
216
+ *
217
+ * // Generate one-time payment address
218
+ * const { stealthAddress } = generateStealthAddress(metaAddress)
219
+ *
220
+ * // Use the stealth address in your transaction
221
+ * await sendPayment({
222
+ * to: stealthAddress.address, // One-time address
223
+ * amount: '1000000000000000000', // 1 ETH
224
+ * ephemeralKey: stealthAddress.ephemeralPublicKey, // Publish for recipient
225
+ * viewTag: stealthAddress.viewTag, // For efficient scanning
226
+ * })
227
+ * ```
228
+ *
229
+ * @example Integrate with SIP intent
230
+ * ```typescript
231
+ * // In a shielded intent, the recipient stealth address is generated automatically
232
+ * const intent = await sip.createIntent({
233
+ * input: { asset: { chain: 'solana', symbol: 'SOL', address: null, decimals: 9 }, amount: 10n },
234
+ * output: { asset: { chain: 'ethereum', symbol: 'ETH', address: null, decimals: 18 }, minAmount: 0n, maxSlippage: 0.01 },
235
+ * privacy: PrivacyLevel.SHIELDED,
236
+ * recipientMetaAddress: 'sip:ethereum:0x02abc...123:0x03def...456',
237
+ * })
238
+ * // intent.recipientStealth contains the generated stealth address
239
+ * ```
240
+ *
241
+ * @see {@link generateStealthMetaAddress} to create meta-address as recipient
242
+ * @see {@link deriveStealthPrivateKey} for recipient to claim funds
243
+ * @see {@link checkStealthAddress} to scan for incoming payments
133
244
  */
134
245
  export function generateStealthAddress(
135
246
  recipientMetaAddress: StealthMetaAddress,
@@ -303,9 +303,8 @@ export class LedgerWalletAdapter extends BaseWalletAdapter {
303
303
  */
304
304
  private async loadTransport(): Promise<TransportWebUSBType> {
305
305
  try {
306
- // @ts-expect-error - Dynamic import
307
306
  const module = await import('@ledgerhq/hw-transport-webusb')
308
- return module.default
307
+ return module.default as unknown as TransportWebUSBType
309
308
  } catch {
310
309
  throw new HardwareWalletError(
311
310
  'Failed to load Ledger transport. Install @ledgerhq/hw-transport-webusb',