@sip-protocol/sdk 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/browser.d.mts +2 -2
  2. package/dist/browser.d.ts +2 -2
  3. package/dist/browser.js +1019 -146
  4. package/dist/browser.mjs +49 -1
  5. package/dist/chunk-AOZIY3GU.mjs +12995 -0
  6. package/dist/chunk-BCLIX5T2.mjs +12940 -0
  7. package/dist/chunk-FKXPHKYD.mjs +12955 -0
  8. package/dist/chunk-OPQ2GQIO.mjs +13013 -0
  9. package/dist/index-BcWNakUD.d.ts +7990 -0
  10. package/dist/index-BsKY3Hr0.d.mts +7990 -0
  11. package/dist/index.d.mts +2 -2
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +990 -117
  14. package/dist/index.mjs +49 -1
  15. package/package.json +2 -1
  16. package/src/adapters/near-intents.ts +8 -0
  17. package/src/bitcoin/index.ts +51 -0
  18. package/src/bitcoin/silent-payments.ts +865 -0
  19. package/src/bitcoin/taproot.ts +590 -0
  20. package/src/cosmos/ibc-stealth.ts +825 -0
  21. package/src/cosmos/index.ts +83 -0
  22. package/src/cosmos/stealth.ts +487 -0
  23. package/src/index.ts +51 -0
  24. package/src/move/aptos.ts +369 -0
  25. package/src/move/index.ts +35 -0
  26. package/src/move/sui.ts +367 -0
  27. package/src/oracle/types.ts +8 -0
  28. package/src/settlement/backends/direct-chain.ts +8 -0
  29. package/src/stealth.ts +3 -3
  30. package/src/validation.ts +42 -1
  31. package/src/wallet/aptos/adapter.ts +422 -0
  32. package/src/wallet/aptos/index.ts +10 -0
  33. package/src/wallet/aptos/mock.ts +410 -0
  34. package/src/wallet/aptos/types.ts +278 -0
  35. package/src/wallet/bitcoin/adapter.ts +470 -0
  36. package/src/wallet/bitcoin/index.ts +38 -0
  37. package/src/wallet/bitcoin/mock.ts +516 -0
  38. package/src/wallet/bitcoin/types.ts +274 -0
  39. package/src/wallet/cosmos/adapter.ts +484 -0
  40. package/src/wallet/cosmos/index.ts +63 -0
  41. package/src/wallet/cosmos/mock.ts +596 -0
  42. package/src/wallet/cosmos/types.ts +462 -0
  43. package/src/wallet/index.ts +127 -0
  44. package/src/wallet/sui/adapter.ts +471 -0
  45. package/src/wallet/sui/index.ts +10 -0
  46. package/src/wallet/sui/mock.ts +439 -0
  47. package/src/wallet/sui/types.ts +245 -0
@@ -0,0 +1,367 @@
1
+ /**
2
+ * Sui stealth address implementation
3
+ *
4
+ * Sui uses ed25519 for signatures, similar to Aptos, Solana, and NEAR.
5
+ * This module provides stealth address generation and address format conversion for Sui.
6
+ *
7
+ * Key differences from Aptos:
8
+ * - Sui addresses are derived via BLAKE2b-256(0x00 || pubkey)
9
+ * - 32-byte addresses encoded as hex with 0x prefix
10
+ * - Signature scheme flag byte (0x00) comes BEFORE public key
11
+ *
12
+ * @see https://docs.sui.io/concepts/cryptography/transaction-auth/signatures
13
+ */
14
+
15
+ import { ed25519 } from '@noble/curves/ed25519'
16
+ import { BLAKE2b } from '@noble/hashes/blake2b'
17
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
18
+ import type { HexString, StealthMetaAddress, StealthAddress } from '@sip-protocol/types'
19
+ import { ValidationError } from '../errors'
20
+ import { isValidHex, isValidEd25519PublicKey } from '../validation'
21
+ import {
22
+ generateEd25519StealthAddress,
23
+ deriveEd25519StealthPrivateKey,
24
+ checkEd25519StealthAddress,
25
+ } from '../stealth'
26
+
27
+ /**
28
+ * Result of Sui stealth address generation
29
+ */
30
+ export interface SuiStealthResult {
31
+ /** Sui address in 0x-prefixed 64-character hex format */
32
+ stealthAddress: string
33
+ /** Raw 32-byte ed25519 stealth public key (hex) */
34
+ stealthPublicKey: HexString
35
+ /** Ephemeral public key for recipient scanning (hex) */
36
+ ephemeralPublicKey: HexString
37
+ /** View tag for efficient scanning (0-255) */
38
+ viewTag: number
39
+ /** Shared secret hash (for verification) */
40
+ sharedSecret: HexString
41
+ }
42
+
43
+ /**
44
+ * Sui signature scheme flag
45
+ * 0x00 = ED25519 signature
46
+ */
47
+ const SUI_ED25519_SCHEME = 0x00
48
+
49
+ /**
50
+ * Convert an ed25519 public key to a Sui address
51
+ *
52
+ * Sui address derivation:
53
+ * 1. Take the 32-byte ed25519 public key
54
+ * 2. Prepend the scheme byte (0x00 for ED25519)
55
+ * 3. Hash with BLAKE2b-256: address = blake2b_256(0x00 || pubkey)
56
+ * 4. Encode as 0x-prefixed hex string (64 characters)
57
+ *
58
+ * @param publicKey - 32-byte ed25519 public key as hex string (with 0x prefix)
59
+ * @returns Sui address (0x-prefixed, 64 hex characters)
60
+ * @throws {ValidationError} If public key is invalid
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const suiAddress = ed25519PublicKeyToSuiAddress('0xabc123...')
65
+ * // Returns: "0x1234...abcd" (64 hex chars)
66
+ * ```
67
+ */
68
+ export function ed25519PublicKeyToSuiAddress(publicKey: HexString): string {
69
+ // Validate input
70
+ if (!isValidHex(publicKey)) {
71
+ throw new ValidationError(
72
+ 'publicKey must be a valid hex string with 0x prefix',
73
+ 'publicKey'
74
+ )
75
+ }
76
+
77
+ if (!isValidEd25519PublicKey(publicKey)) {
78
+ throw new ValidationError(
79
+ 'publicKey must be 32 bytes (64 hex characters)',
80
+ 'publicKey'
81
+ )
82
+ }
83
+
84
+ // Convert hex to bytes (remove 0x prefix)
85
+ const publicKeyBytes = hexToBytes(publicKey.slice(2))
86
+
87
+ // Prepend signature scheme byte (0x00 for ED25519)
88
+ const addressInput = new Uint8Array(publicKeyBytes.length + 1)
89
+ addressInput[0] = SUI_ED25519_SCHEME
90
+ addressInput.set(publicKeyBytes, 1)
91
+
92
+ // Hash with BLAKE2b-256 to get the address
93
+ const hasher = new BLAKE2b({ dkLen: 32 })
94
+ hasher.update(addressInput)
95
+ const addressHash = hasher.digest()
96
+
97
+ // Return as 0x-prefixed hex string
98
+ return `0x${bytesToHex(addressHash)}`
99
+ }
100
+
101
+ /**
102
+ * Validate a Sui address format
103
+ *
104
+ * Checks that the address:
105
+ * - Is a valid hex string with 0x prefix
106
+ * - Is exactly 32 bytes (64 hex characters)
107
+ * - Contains only valid hex characters
108
+ *
109
+ * Note: This does NOT verify if the address exists on-chain or is derived correctly.
110
+ * It only validates the format.
111
+ *
112
+ * @param address - Sui address to validate
113
+ * @returns true if format is valid, false otherwise
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * isValidSuiAddress('0x1234...abcd') // true (64 hex chars)
118
+ * isValidSuiAddress('0x123') // false (too short)
119
+ * isValidSuiAddress('1234abcd...') // false (no 0x prefix)
120
+ * ```
121
+ */
122
+ export function isValidSuiAddress(address: string): boolean {
123
+ if (typeof address !== 'string' || address.length === 0) {
124
+ return false
125
+ }
126
+
127
+ // Must start with 0x
128
+ if (!address.startsWith('0x')) {
129
+ return false
130
+ }
131
+
132
+ // Must be exactly 64 hex characters after 0x (32 bytes)
133
+ const hexPart = address.slice(2)
134
+ if (hexPart.length !== 64) {
135
+ return false
136
+ }
137
+
138
+ // Must be valid hex
139
+ return /^[0-9a-fA-F]{64}$/.test(hexPart)
140
+ }
141
+
142
+ /**
143
+ * Normalize a Sui address to lowercase
144
+ *
145
+ * @param address - Sui address (0x-prefixed hex string)
146
+ * @returns Normalized address as HexString
147
+ * @throws {ValidationError} If address format is invalid
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const normalized = normalizeSuiAddress('0x1234...ABCD')
152
+ * // Returns: "0x1234...abcd" (lowercase)
153
+ * ```
154
+ */
155
+ export function normalizeSuiAddress(address: string): HexString {
156
+ if (!isValidSuiAddress(address)) {
157
+ throw new ValidationError(
158
+ 'Invalid Sui address format (must be 0x-prefixed 64 hex characters)',
159
+ 'address'
160
+ )
161
+ }
162
+
163
+ // Normalize to lowercase
164
+ return address.toLowerCase() as HexString
165
+ }
166
+
167
+ /**
168
+ * Generate a stealth address for Sui
169
+ *
170
+ * Uses the existing ed25519 stealth address generation logic and converts
171
+ * the resulting ed25519 public key to Sui address format.
172
+ *
173
+ * @param recipientMetaAddress - Recipient's stealth meta-address (must be chain: 'sui')
174
+ * @returns Sui stealth address result with all necessary data
175
+ * @throws {ValidationError} If meta-address is invalid or not for Sui
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const metaAddress = {
180
+ * spendingKey: '0x...',
181
+ * viewingKey: '0x...',
182
+ * chain: 'sui'
183
+ * }
184
+ * const result = generateSuiStealthAddress(metaAddress)
185
+ * console.log(result.stealthAddress) // "0x1234...abcd"
186
+ * console.log(result.viewTag) // 42
187
+ * ```
188
+ */
189
+ export function generateSuiStealthAddress(
190
+ recipientMetaAddress: StealthMetaAddress,
191
+ ): SuiStealthResult {
192
+ // Validate chain
193
+ if (recipientMetaAddress.chain !== 'sui') {
194
+ throw new ValidationError(
195
+ `Expected chain 'sui', got '${recipientMetaAddress.chain}'`,
196
+ 'recipientMetaAddress.chain'
197
+ )
198
+ }
199
+
200
+ // Generate ed25519 stealth address using existing logic
201
+ const { stealthAddress, sharedSecret } = generateEd25519StealthAddress(recipientMetaAddress)
202
+
203
+ // Convert the ed25519 public key to Sui address format
204
+ const suiAddress = ed25519PublicKeyToSuiAddress(stealthAddress.address)
205
+
206
+ return {
207
+ stealthAddress: suiAddress,
208
+ stealthPublicKey: stealthAddress.address,
209
+ ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
210
+ viewTag: stealthAddress.viewTag,
211
+ sharedSecret,
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Derive the private key for a Sui stealth address
217
+ *
218
+ * This allows the recipient to claim funds sent to a stealth address.
219
+ * Uses the standard ed25519 stealth key derivation.
220
+ *
221
+ * @param stealthAddress - The stealth address data (from announcement)
222
+ * @param spendingPrivateKey - Recipient's spending private key
223
+ * @param viewingPrivateKey - Recipient's viewing private key
224
+ * @returns Derived private key (raw scalar, little-endian format)
225
+ * @throws {ValidationError} If any input is invalid
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * const recovery = deriveSuiStealthPrivateKey(
230
+ * stealthAddress,
231
+ * spendingPrivKey,
232
+ * viewingPrivKey
233
+ * )
234
+ * // Use recovery.privateKey to sign transactions
235
+ * ```
236
+ */
237
+ export function deriveSuiStealthPrivateKey(
238
+ stealthAddress: StealthAddress,
239
+ spendingPrivateKey: HexString,
240
+ viewingPrivateKey: HexString,
241
+ ): {
242
+ stealthAddress: HexString
243
+ ephemeralPublicKey: HexString
244
+ privateKey: HexString
245
+ suiAddress: string
246
+ } {
247
+ // Use standard ed25519 derivation
248
+ const recovery = deriveEd25519StealthPrivateKey(
249
+ stealthAddress,
250
+ spendingPrivateKey,
251
+ viewingPrivateKey
252
+ )
253
+
254
+ // Convert the stealth public key to Sui address
255
+ const suiAddress = ed25519PublicKeyToSuiAddress(recovery.stealthAddress)
256
+
257
+ return {
258
+ ...recovery,
259
+ suiAddress,
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Check if a stealth address belongs to this recipient
265
+ *
266
+ * Uses view tag for efficient filtering before full computation.
267
+ * This is the same as the standard ed25519 check since Sui stealth
268
+ * addresses use ed25519 stealth public keys.
269
+ *
270
+ * @param stealthAddress - Stealth address to check
271
+ * @param spendingPrivateKey - Recipient's spending private key
272
+ * @param viewingPrivateKey - Recipient's viewing private key
273
+ * @returns true if this address belongs to the recipient
274
+ * @throws {ValidationError} If any input is invalid
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const isMine = checkSuiStealthAddress(
279
+ * stealthAddress,
280
+ * mySpendingPrivKey,
281
+ * myViewingPrivKey
282
+ * )
283
+ * ```
284
+ */
285
+ export function checkSuiStealthAddress(
286
+ stealthAddress: StealthAddress,
287
+ spendingPrivateKey: HexString,
288
+ viewingPrivateKey: HexString,
289
+ ): boolean {
290
+ // Use standard ed25519 check
291
+ return checkEd25519StealthAddress(
292
+ stealthAddress,
293
+ spendingPrivateKey,
294
+ viewingPrivateKey
295
+ )
296
+ }
297
+
298
+ /**
299
+ * Sui Stealth Service
300
+ *
301
+ * Provides a convenient class-based interface for Sui stealth address operations.
302
+ * This is useful when you need to perform multiple operations or want to encapsulate
303
+ * the logic in a service object.
304
+ */
305
+ export class SuiStealthService {
306
+ /**
307
+ * Generate a stealth address for a Sui recipient
308
+ *
309
+ * @param recipientMetaAddress - Recipient's stealth meta-address
310
+ * @returns Complete stealth address result
311
+ */
312
+ generateStealthAddress(recipientMetaAddress: StealthMetaAddress): SuiStealthResult {
313
+ return generateSuiStealthAddress(recipientMetaAddress)
314
+ }
315
+
316
+ /**
317
+ * Convert an ed25519 public key to Sui address format
318
+ *
319
+ * @param publicKey - 32-byte ed25519 public key
320
+ * @returns Sui address string
321
+ */
322
+ stealthKeyToSuiAddress(publicKey: HexString): string {
323
+ return ed25519PublicKeyToSuiAddress(publicKey)
324
+ }
325
+
326
+ /**
327
+ * Derive the private key for a stealth address
328
+ *
329
+ * @param stealthAddress - Stealth address data
330
+ * @param spendingPrivateKey - Recipient's spending private key
331
+ * @param viewingPrivateKey - Recipient's viewing private key
332
+ * @returns Recovery data with derived private key
333
+ */
334
+ deriveStealthPrivateKey(
335
+ stealthAddress: StealthAddress,
336
+ spendingPrivateKey: HexString,
337
+ viewingPrivateKey: HexString,
338
+ ) {
339
+ return deriveSuiStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey)
340
+ }
341
+
342
+ /**
343
+ * Check if a stealth address belongs to this recipient
344
+ *
345
+ * @param stealthAddress - Stealth address to check
346
+ * @param spendingPrivateKey - Recipient's spending private key
347
+ * @param viewingPrivateKey - Recipient's viewing private key
348
+ * @returns true if the address belongs to this recipient
349
+ */
350
+ checkStealthAddress(
351
+ stealthAddress: StealthAddress,
352
+ spendingPrivateKey: HexString,
353
+ viewingPrivateKey: HexString,
354
+ ): boolean {
355
+ return checkSuiStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey)
356
+ }
357
+
358
+ /**
359
+ * Validate a Sui address format
360
+ *
361
+ * @param address - Address to validate
362
+ * @returns true if valid format
363
+ */
364
+ isValidAddress(address: string): boolean {
365
+ return isValidSuiAddress(address)
366
+ }
367
+ }
@@ -254,4 +254,12 @@ export const CHAIN_NUMERIC_IDS: Record<ChainId, number> = {
254
254
  solana: 501, // Non-standard, SIP-specific
255
255
  near: 502, // Non-standard, SIP-specific
256
256
  zcash: 503, // Non-standard, SIP-specific
257
+ aptos: 504, // Non-standard, SIP-specific
258
+ sui: 505, // Non-standard, SIP-specific
259
+ cosmos: 506, // Non-standard, SIP-specific (Cosmos Hub)
260
+ osmosis: 507, // Non-standard, SIP-specific
261
+ injective: 508, // Non-standard, SIP-specific
262
+ celestia: 509, // Non-standard, SIP-specific
263
+ sei: 510, // Non-standard, SIP-specific
264
+ dydx: 511, // Non-standard, SIP-specific
257
265
  }
@@ -108,6 +108,14 @@ const DEFAULT_GAS_FEES: Record<ChainId, bigint> = {
108
108
  optimism: 21000000n, // 21k gas * 0.001 gwei ETH = 21 gwei = 21e6 wei
109
109
  base: 2100000000n, // 21k gas * 0.1 gwei ETH = 2100 gwei = 2.1e9 wei
110
110
  bitcoin: 10000n, // 10000 sats = 0.0001 BTC (estimate for 1 input, 2 outputs)
111
+ aptos: 100n, // 0.000001 APT in octas (gas units)
112
+ sui: 1000n, // 0.000001 SUI in MIST
113
+ cosmos: 5000n, // 0.005 ATOM in uatom
114
+ osmosis: 5000n, // 0.005 OSMO in uosmo
115
+ injective: 5000n, // 0.000005 INJ in inj (18 decimals)
116
+ celestia: 5000n, // 0.005 TIA in utia
117
+ sei: 5000n, // 0.005 SEI in usei
118
+ dydx: 5000n, // 0.000005 DYDX in dydx (18 decimals)
111
119
  }
112
120
 
113
121
  /**
package/src/stealth.ts CHANGED
@@ -54,7 +54,7 @@ export function generateStealthMetaAddress(
54
54
  // Validate chain
55
55
  if (!isValidChainId(chain)) {
56
56
  throw new ValidationError(
57
- `invalid chain '${chain}', must be one of: solana, ethereum, near, zcash, polygon, arbitrum, optimism, base`,
57
+ `invalid chain '${chain}', must be one of: solana, ethereum, near, zcash, polygon, arbitrum, optimism, base, bitcoin, aptos, sui, cosmos, osmosis, injective, celestia, sei, dydx`,
58
58
  'chain'
59
59
  )
60
60
  }
@@ -554,7 +554,7 @@ const ED25519_ORDER = 2n ** 252n + 27742317777372353535851937790883648493n
554
554
  /**
555
555
  * Chains that use ed25519 for stealth addresses
556
556
  */
557
- const ED25519_CHAINS: ChainId[] = ['solana', 'near']
557
+ const ED25519_CHAINS: ChainId[] = ['solana', 'near', 'aptos', 'sui']
558
558
 
559
559
  /**
560
560
  * Check if a chain uses ed25519 for stealth addresses
@@ -726,7 +726,7 @@ export function generateEd25519StealthMetaAddress(
726
726
  // Validate chain
727
727
  if (!isValidChainId(chain)) {
728
728
  throw new ValidationError(
729
- `invalid chain '${chain}', must be one of: solana, ethereum, near, zcash, polygon, arbitrum, optimism, base`,
729
+ `invalid chain '${chain}', must be one of: solana, ethereum, near, zcash, polygon, arbitrum, optimism, base, bitcoin, aptos, sui, cosmos, osmosis, injective, celestia, sei, dydx`,
730
730
  'chain'
731
731
  )
732
732
  }
package/src/validation.ts CHANGED
@@ -29,6 +29,15 @@ const VALID_CHAIN_IDS: readonly ChainId[] = [
29
29
  'arbitrum',
30
30
  'optimism',
31
31
  'base',
32
+ 'bitcoin',
33
+ 'aptos',
34
+ 'sui',
35
+ 'cosmos',
36
+ 'osmosis',
37
+ 'injective',
38
+ 'celestia',
39
+ 'sei',
40
+ 'dydx',
32
41
  ] as const
33
42
 
34
43
  /**
@@ -414,10 +423,25 @@ export function isValidNearAddressFormat(address: string): boolean {
414
423
  return true
415
424
  }
416
425
 
426
+ /**
427
+ * Check if an address is a valid Cosmos bech32 address
428
+ */
429
+ export function isValidCosmosAddressFormat(address: string): boolean {
430
+ if (typeof address !== 'string') return false
431
+
432
+ // Bech32 addresses follow pattern: prefix1[alphanumeric]
433
+ // Typical length: 39-90 characters
434
+ if (address.length < 39 || address.length > 90) return false
435
+
436
+ // Check for valid bech32 format: prefix + '1' + data
437
+ const bech32Pattern = /^[a-z]+1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38,}$/
438
+ return bech32Pattern.test(address)
439
+ }
440
+
417
441
  /**
418
442
  * Get the expected address format for a chain
419
443
  */
420
- export function getChainAddressType(chain: ChainId): 'evm' | 'solana' | 'near' | 'zcash' | 'unknown' {
444
+ export function getChainAddressType(chain: ChainId): 'evm' | 'solana' | 'near' | 'zcash' | 'cosmos' | 'unknown' {
421
445
  switch (chain) {
422
446
  case 'ethereum':
423
447
  case 'polygon':
@@ -431,6 +455,13 @@ export function getChainAddressType(chain: ChainId): 'evm' | 'solana' | 'near' |
431
455
  return 'near'
432
456
  case 'zcash':
433
457
  return 'zcash'
458
+ case 'cosmos':
459
+ case 'osmosis':
460
+ case 'injective':
461
+ case 'celestia':
462
+ case 'sei':
463
+ case 'dydx':
464
+ return 'cosmos'
434
465
  default:
435
466
  return 'unknown'
436
467
  }
@@ -485,6 +516,16 @@ export function validateAddressForChain(address: string, chain: ChainId, field:
485
516
  )
486
517
  }
487
518
  break
519
+ case 'cosmos':
520
+ // Cosmos chains use bech32 encoding - validate basic format
521
+ if (!isValidCosmosAddressFormat(address)) {
522
+ throw new ValidationError(
523
+ `Invalid address format for ${chain}. Expected Cosmos bech32 address, got: ${address.slice(0, 20)}...`,
524
+ field,
525
+ { chain, expectedFormat: 'bech32' }
526
+ )
527
+ }
528
+ break
488
529
  default:
489
530
  // Unknown chain - skip validation
490
531
  break