@sip-protocol/sdk 0.7.2 → 0.7.4

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
  4. package/dist/browser.d.mts +10 -4
  5. package/dist/browser.d.ts +10 -4
  6. package/dist/browser.js +48874 -18336
  7. package/dist/browser.mjs +674 -48
  8. package/dist/chunk-4GRJ5MAW.mjs +152 -0
  9. package/dist/chunk-5D7A3L3W.mjs +717 -0
  10. package/dist/chunk-64AYA5F5.mjs +7834 -0
  11. package/dist/chunk-GMDGB22A.mjs +379 -0
  12. package/dist/chunk-I534WKN7.mjs +328 -0
  13. package/dist/chunk-IBZVA5Y7.mjs +1003 -0
  14. package/dist/chunk-PRRZAWJE.mjs +223 -0
  15. package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
  16. package/dist/chunk-YWGJ77A2.mjs +33806 -0
  17. package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
  18. package/dist/constants-LHAAUC2T.mjs +51 -0
  19. package/dist/dist-2OGQ7FED.mjs +3957 -0
  20. package/dist/dist-IFHPYLDX.mjs +254 -0
  21. package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
  22. package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
  23. package/dist/index-DXh2IGkz.d.ts +24681 -0
  24. package/dist/index-DeE1ZzA4.d.mts +24681 -0
  25. package/dist/index.d.mts +9 -3
  26. package/dist/index.d.ts +9 -3
  27. package/dist/index.js +48676 -17318
  28. package/dist/index.mjs +583 -19
  29. package/dist/interface-Bf7w1PLW.d.mts +679 -0
  30. package/dist/interface-Bf7w1PLW.d.ts +679 -0
  31. package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
  32. package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
  33. package/dist/proofs/halo2.d.mts +151 -0
  34. package/dist/proofs/halo2.d.ts +151 -0
  35. package/dist/proofs/halo2.js +350 -0
  36. package/dist/proofs/halo2.mjs +11 -0
  37. package/dist/proofs/kimchi.d.mts +160 -0
  38. package/dist/proofs/kimchi.d.ts +160 -0
  39. package/dist/proofs/kimchi.js +431 -0
  40. package/dist/proofs/kimchi.mjs +13 -0
  41. package/dist/proofs/noir.d.mts +1 -1
  42. package/dist/proofs/noir.d.ts +1 -1
  43. package/dist/proofs/noir.js +74 -18
  44. package/dist/proofs/noir.mjs +84 -24
  45. package/dist/solana-U3MEGU7W.mjs +280 -0
  46. package/dist/validity_proof-3POXLPNY.mjs +21 -0
  47. package/package.json +54 -21
  48. package/src/adapters/index.ts +41 -0
  49. package/src/adapters/jupiter.ts +571 -0
  50. package/src/adapters/near-intents.ts +135 -0
  51. package/src/advisor/advisor.ts +653 -0
  52. package/src/advisor/index.ts +54 -0
  53. package/src/advisor/tools.ts +303 -0
  54. package/src/advisor/types.ts +164 -0
  55. package/src/chains/ethereum/announcement.ts +536 -0
  56. package/src/chains/ethereum/bnb-optimizations.ts +474 -0
  57. package/src/chains/ethereum/commitment.ts +522 -0
  58. package/src/chains/ethereum/constants.ts +462 -0
  59. package/src/chains/ethereum/deployment.ts +596 -0
  60. package/src/chains/ethereum/gas-estimation.ts +538 -0
  61. package/src/chains/ethereum/index.ts +268 -0
  62. package/src/chains/ethereum/optimizations.ts +614 -0
  63. package/src/chains/ethereum/privacy-adapter.ts +855 -0
  64. package/src/chains/ethereum/registry.ts +584 -0
  65. package/src/chains/ethereum/rpc.ts +905 -0
  66. package/src/chains/ethereum/stealth.ts +491 -0
  67. package/src/chains/ethereum/token.ts +790 -0
  68. package/src/chains/ethereum/transfer.ts +637 -0
  69. package/src/chains/ethereum/types.ts +456 -0
  70. package/src/chains/ethereum/viewing-key.ts +455 -0
  71. package/src/chains/near/commitment.ts +608 -0
  72. package/src/chains/near/constants.ts +284 -0
  73. package/src/chains/near/function-call.ts +871 -0
  74. package/src/chains/near/history.ts +654 -0
  75. package/src/chains/near/implicit-account.ts +840 -0
  76. package/src/chains/near/index.ts +393 -0
  77. package/src/chains/near/native-transfer.ts +658 -0
  78. package/src/chains/near/nep141.ts +775 -0
  79. package/src/chains/near/privacy-adapter.ts +889 -0
  80. package/src/chains/near/resolver.ts +971 -0
  81. package/src/chains/near/rpc.ts +1016 -0
  82. package/src/chains/near/stealth.ts +419 -0
  83. package/src/chains/near/types.ts +317 -0
  84. package/src/chains/near/viewing-key.ts +876 -0
  85. package/src/chains/solana/anchor-transfer.ts +386 -0
  86. package/src/chains/solana/commitment.ts +577 -0
  87. package/src/chains/solana/constants.ts +126 -12
  88. package/src/chains/solana/ephemeral-keys.ts +543 -0
  89. package/src/chains/solana/index.ts +276 -1
  90. package/src/chains/solana/key-derivation.ts +418 -0
  91. package/src/chains/solana/kit-compat.ts +334 -0
  92. package/src/chains/solana/optimizations.ts +560 -0
  93. package/src/chains/solana/privacy-adapter.ts +605 -0
  94. package/src/chains/solana/providers/generic.ts +201 -0
  95. package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
  96. package/src/chains/solana/providers/helius-enhanced.ts +623 -0
  97. package/src/chains/solana/providers/helius.ts +402 -0
  98. package/src/chains/solana/providers/index.ts +85 -0
  99. package/src/chains/solana/providers/interface.ts +221 -0
  100. package/src/chains/solana/providers/quicknode.ts +409 -0
  101. package/src/chains/solana/providers/triton.ts +426 -0
  102. package/src/chains/solana/providers/webhook.ts +790 -0
  103. package/src/chains/solana/rpc-client.ts +1150 -0
  104. package/src/chains/solana/scan.ts +170 -73
  105. package/src/chains/solana/sol-transfer.ts +732 -0
  106. package/src/chains/solana/spl-transfer.ts +886 -0
  107. package/src/chains/solana/stealth-scanner.ts +703 -0
  108. package/src/chains/solana/sunspot-verifier.ts +453 -0
  109. package/src/chains/solana/transaction-builder.ts +755 -0
  110. package/src/chains/solana/transfer.ts +74 -5
  111. package/src/chains/solana/types.ts +77 -7
  112. package/src/chains/solana/utils.ts +110 -0
  113. package/src/chains/solana/viewing-key.ts +807 -0
  114. package/src/compliance/fireblocks.ts +921 -0
  115. package/src/compliance/index.ts +37 -0
  116. package/src/compliance/range-sas.ts +956 -0
  117. package/src/config/endpoints.ts +100 -0
  118. package/src/crypto.ts +11 -8
  119. package/src/errors.ts +82 -0
  120. package/src/evm/erc4337-relayer.ts +830 -0
  121. package/src/evm/index.ts +47 -0
  122. package/src/fees/calculator.ts +396 -0
  123. package/src/fees/index.ts +87 -0
  124. package/src/fees/near-contract.ts +429 -0
  125. package/src/fees/types.ts +268 -0
  126. package/src/index.ts +785 -1
  127. package/src/intent.ts +6 -3
  128. package/src/logger.ts +324 -0
  129. package/src/network/index.ts +80 -0
  130. package/src/network/proxy.ts +691 -0
  131. package/src/optimizations/index.ts +541 -0
  132. package/src/oracle/types.ts +1 -0
  133. package/src/privacy-backends/arcium-types.ts +727 -0
  134. package/src/privacy-backends/arcium.ts +719 -0
  135. package/src/privacy-backends/combined-privacy.ts +866 -0
  136. package/src/privacy-backends/cspl-token.ts +595 -0
  137. package/src/privacy-backends/cspl-types.ts +512 -0
  138. package/src/privacy-backends/cspl.ts +907 -0
  139. package/src/privacy-backends/health.ts +488 -0
  140. package/src/privacy-backends/inco-types.ts +323 -0
  141. package/src/privacy-backends/inco.ts +616 -0
  142. package/src/privacy-backends/index.ts +336 -0
  143. package/src/privacy-backends/interface.ts +906 -0
  144. package/src/privacy-backends/lru-cache.ts +343 -0
  145. package/src/privacy-backends/magicblock.ts +458 -0
  146. package/src/privacy-backends/mock.ts +258 -0
  147. package/src/privacy-backends/privacycash-types.ts +278 -0
  148. package/src/privacy-backends/privacycash.ts +456 -0
  149. package/src/privacy-backends/private-swap.ts +570 -0
  150. package/src/privacy-backends/rate-limiter.ts +683 -0
  151. package/src/privacy-backends/registry.ts +690 -0
  152. package/src/privacy-backends/router.ts +626 -0
  153. package/src/privacy-backends/shadowwire.ts +449 -0
  154. package/src/privacy-backends/sip-native.ts +256 -0
  155. package/src/privacy-logger.ts +191 -0
  156. package/src/production-safety.ts +373 -0
  157. package/src/proofs/aggregator.ts +1029 -0
  158. package/src/proofs/browser-composer.ts +1150 -0
  159. package/src/proofs/browser.ts +113 -25
  160. package/src/proofs/cache/index.ts +127 -0
  161. package/src/proofs/cache/interface.ts +545 -0
  162. package/src/proofs/cache/key-generator.ts +188 -0
  163. package/src/proofs/cache/lru-cache.ts +481 -0
  164. package/src/proofs/cache/multi-tier-cache.ts +575 -0
  165. package/src/proofs/cache/persistent-cache.ts +788 -0
  166. package/src/proofs/compliance-proof.ts +872 -0
  167. package/src/proofs/composer/base.ts +923 -0
  168. package/src/proofs/composer/index.ts +25 -0
  169. package/src/proofs/composer/interface.ts +518 -0
  170. package/src/proofs/composer/types.ts +383 -0
  171. package/src/proofs/converters/halo2.ts +452 -0
  172. package/src/proofs/converters/index.ts +208 -0
  173. package/src/proofs/converters/interface.ts +363 -0
  174. package/src/proofs/converters/kimchi.ts +462 -0
  175. package/src/proofs/converters/noir.ts +451 -0
  176. package/src/proofs/fallback.ts +888 -0
  177. package/src/proofs/halo2.ts +42 -0
  178. package/src/proofs/index.ts +471 -0
  179. package/src/proofs/interface.ts +13 -0
  180. package/src/proofs/kimchi.ts +42 -0
  181. package/src/proofs/lazy.ts +1004 -0
  182. package/src/proofs/mock.ts +25 -1
  183. package/src/proofs/noir.ts +111 -30
  184. package/src/proofs/orchestrator.ts +960 -0
  185. package/src/proofs/parallel/concurrency.ts +297 -0
  186. package/src/proofs/parallel/dependency-graph.ts +602 -0
  187. package/src/proofs/parallel/executor.ts +420 -0
  188. package/src/proofs/parallel/index.ts +131 -0
  189. package/src/proofs/parallel/interface.ts +685 -0
  190. package/src/proofs/parallel/worker-pool.ts +644 -0
  191. package/src/proofs/providers/halo2.ts +560 -0
  192. package/src/proofs/providers/index.ts +34 -0
  193. package/src/proofs/providers/kimchi.ts +641 -0
  194. package/src/proofs/validator.ts +881 -0
  195. package/src/proofs/verifier.ts +867 -0
  196. package/src/quantum/index.ts +112 -0
  197. package/src/quantum/winternitz-vault.ts +639 -0
  198. package/src/quantum/wots.ts +611 -0
  199. package/src/settlement/backends/direct-chain.ts +1 -0
  200. package/src/settlement/index.ts +9 -0
  201. package/src/settlement/router.ts +732 -46
  202. package/src/solana/index.ts +72 -0
  203. package/src/solana/jito-relayer.ts +687 -0
  204. package/src/solana/noir-verifier-types.ts +430 -0
  205. package/src/solana/noir-verifier.ts +816 -0
  206. package/src/stealth/address-derivation.ts +193 -0
  207. package/src/stealth/ed25519.ts +431 -0
  208. package/src/stealth/index.ts +233 -0
  209. package/src/stealth/meta-address.ts +221 -0
  210. package/src/stealth/secp256k1.ts +368 -0
  211. package/src/stealth/utils.ts +194 -0
  212. package/src/stealth.ts +50 -1504
  213. package/src/surveillance/algorithms/address-reuse.ts +143 -0
  214. package/src/surveillance/algorithms/cluster.ts +247 -0
  215. package/src/surveillance/algorithms/exchange.ts +295 -0
  216. package/src/surveillance/algorithms/temporal.ts +337 -0
  217. package/src/surveillance/analyzer.ts +442 -0
  218. package/src/surveillance/index.ts +64 -0
  219. package/src/surveillance/scoring.ts +372 -0
  220. package/src/surveillance/types.ts +264 -0
  221. package/src/sync/index.ts +106 -0
  222. package/src/sync/manager.ts +504 -0
  223. package/src/sync/mock-provider.ts +318 -0
  224. package/src/sync/oblivious.ts +625 -0
  225. package/src/tokens/index.ts +15 -0
  226. package/src/tokens/registry.ts +301 -0
  227. package/src/utils/deprecation.ts +94 -0
  228. package/src/utils/index.ts +9 -0
  229. package/src/wallet/ethereum/index.ts +68 -0
  230. package/src/wallet/ethereum/metamask-privacy.ts +420 -0
  231. package/src/wallet/ethereum/multi-wallet.ts +646 -0
  232. package/src/wallet/ethereum/privacy-adapter.ts +700 -0
  233. package/src/wallet/ethereum/types.ts +3 -1
  234. package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
  235. package/src/wallet/hardware/index.ts +10 -0
  236. package/src/wallet/hardware/ledger-privacy.ts +414 -0
  237. package/src/wallet/index.ts +71 -0
  238. package/src/wallet/near/adapter.ts +626 -0
  239. package/src/wallet/near/index.ts +86 -0
  240. package/src/wallet/near/meteor-wallet.ts +1153 -0
  241. package/src/wallet/near/my-near-wallet.ts +790 -0
  242. package/src/wallet/near/wallet-selector.ts +702 -0
  243. package/src/wallet/solana/adapter.ts +6 -4
  244. package/src/wallet/solana/index.ts +13 -0
  245. package/src/wallet/solana/privacy-adapter.ts +567 -0
  246. package/src/wallet/sui/types.ts +6 -4
  247. package/src/zcash/rpc-client.ts +13 -6
  248. package/dist/chunk-3INS3PR5.mjs +0 -884
  249. package/dist/chunk-3OVABDRH.mjs +0 -17096
  250. package/dist/chunk-DLDWZFYC.mjs +0 -1495
  251. package/dist/chunk-E6SZWREQ.mjs +0 -57
  252. package/dist/chunk-G33LB27A.mjs +0 -16166
  253. package/dist/chunk-HGU6HZRC.mjs +0 -231
  254. package/dist/chunk-L2K34JCU.mjs +0 -1496
  255. package/dist/chunk-SN4ZDTVW.mjs +0 -16166
  256. package/dist/constants-VOI7BSLK.mjs +0 -27
  257. package/dist/index-BYZbDjal.d.ts +0 -11390
  258. package/dist/index-CHB3KuOB.d.mts +0 -11859
  259. package/dist/index-CzWPI6Le.d.ts +0 -11859
  260. package/dist/index-xbWjohNq.d.mts +0 -11390
  261. package/dist/solana-5EMCTPTS.mjs +0 -46
  262. package/dist/solana-Q4NAVBTS.mjs +0 -46
@@ -0,0 +1,700 @@
1
+ /**
2
+ * Privacy-Extended Ethereum Wallet Adapter
3
+ *
4
+ * Extends EthereumWalletAdapter with stealth address capabilities for
5
+ * same-chain privacy operations using secp256k1 curve.
6
+ *
7
+ * @module wallet/ethereum/privacy-adapter
8
+ */
9
+
10
+ import type {
11
+ HexString,
12
+ StealthMetaAddress,
13
+ StealthAddress,
14
+ } from '@sip-protocol/types'
15
+ import { sha256 } from '@noble/hashes/sha2'
16
+ import { secp256k1 } from '@noble/curves/secp256k1'
17
+ import { keccak_256 } from '@noble/hashes/sha3'
18
+ import { EthereumWalletAdapter } from './adapter'
19
+ import type { EthereumAdapterConfig, EIP712TypedData } from './types'
20
+ import {
21
+ generateEthereumStealthMetaAddress,
22
+ generateEthereumStealthAddress,
23
+ deriveEthereumStealthPrivateKey,
24
+ checkEthereumStealthAddress,
25
+ stealthPublicKeyToEthAddress,
26
+ encodeEthereumStealthMetaAddress,
27
+ parseEthereumStealthMetaAddress,
28
+ type EthereumStealthMetaAddress,
29
+ } from '../../chains/ethereum/stealth'
30
+
31
+ // ─── Types ──────────────────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Configuration for privacy adapter
35
+ */
36
+ export interface PrivacyEthereumAdapterConfig extends EthereumAdapterConfig {
37
+ /**
38
+ * Pre-existing stealth meta-address to use
39
+ * If not provided, one will be generated on first use
40
+ */
41
+ metaAddress?: StealthMetaAddress | EthereumStealthMetaAddress | string
42
+ /**
43
+ * Pre-existing spending private key
44
+ * Required if metaAddress is provided
45
+ */
46
+ spendingPrivateKey?: HexString
47
+ /**
48
+ * Pre-existing viewing private key
49
+ * Required if metaAddress is provided
50
+ */
51
+ viewingPrivateKey?: HexString
52
+ /**
53
+ * Derive stealth keys from wallet signature
54
+ * If true, keys are derived deterministically from wallet
55
+ * If false (default), random keys are generated
56
+ */
57
+ deriveFromWallet?: boolean
58
+ /**
59
+ * Domain separation string for key derivation
60
+ * Used when deriveFromWallet is true
61
+ */
62
+ derivationDomain?: string
63
+ }
64
+
65
+ /**
66
+ * Stealth key material for Ethereum
67
+ */
68
+ export interface EthereumStealthKeyMaterial {
69
+ metaAddress: StealthMetaAddress
70
+ spendingPrivateKey: HexString
71
+ viewingPrivateKey: HexString
72
+ /** Encoded EIP-5564 format */
73
+ encodedMetaAddress: string
74
+ }
75
+
76
+ /**
77
+ * Scanned stealth payment
78
+ */
79
+ export interface EthereumScannedPayment {
80
+ /** Stealth address that received the payment */
81
+ stealthAddress: HexString
82
+ /** Ephemeral public key from sender */
83
+ ephemeralPublicKey: HexString
84
+ /** View tag for fast scanning */
85
+ viewTag: number
86
+ /** Whether this payment belongs to us */
87
+ isOwned: boolean
88
+ /** Ethereum address format of stealth address */
89
+ ethAddress: HexString
90
+ }
91
+
92
+ /**
93
+ * Result of claiming a stealth payment
94
+ */
95
+ export interface EthereumClaimResult {
96
+ /** Derived private key for spending */
97
+ privateKey: HexString
98
+ /** Public key (compressed) */
99
+ publicKey: HexString
100
+ /** Ethereum address */
101
+ ethAddress: HexString
102
+ }
103
+
104
+ /**
105
+ * Privacy context that persists across reconnections
106
+ */
107
+ export interface PrivacyContext {
108
+ /** Current stealth keys */
109
+ keys: EthereumStealthKeyMaterial
110
+ /** Whether keys were derived from wallet */
111
+ derivedFromWallet: boolean
112
+ /** Derivation domain if applicable */
113
+ derivationDomain?: string
114
+ /** Wallet address keys were derived for */
115
+ derivedForAddress?: string
116
+ }
117
+
118
+ // ─── Privacy Adapter ────────────────────────────────────────────────────────
119
+
120
+ /**
121
+ * Privacy-enabled Ethereum wallet adapter
122
+ *
123
+ * Extends the base EthereumWalletAdapter with:
124
+ * - Stealth meta-address generation and management
125
+ * - Stealth address derivation for receiving
126
+ * - Private key derivation for spending
127
+ * - Payment scanning with view tags
128
+ * - EIP-712 signing for permit operations
129
+ *
130
+ * @example Generate stealth address for receiving
131
+ * ```typescript
132
+ * const wallet = new PrivacyEthereumWalletAdapter({ wallet: 'metamask' })
133
+ * await wallet.connect()
134
+ * await wallet.initializePrivacy()
135
+ *
136
+ * // Share meta-address with senders
137
+ * const metaAddress = wallet.getMetaAddress()
138
+ * console.log('Send to:', wallet.getEncodedMetaAddress())
139
+ * ```
140
+ *
141
+ * @example Derive from wallet (deterministic)
142
+ * ```typescript
143
+ * const wallet = new PrivacyEthereumWalletAdapter({
144
+ * wallet: 'metamask',
145
+ * deriveFromWallet: true,
146
+ * derivationDomain: 'my-app.com',
147
+ * })
148
+ * await wallet.connect()
149
+ * await wallet.initializePrivacy()
150
+ * // Keys derived deterministically from wallet signature
151
+ * ```
152
+ *
153
+ * @example Scan and claim payments
154
+ * ```typescript
155
+ * const payments = wallet.scanPayments([announcement1, announcement2])
156
+ * for (const payment of payments.filter(p => p.isOwned)) {
157
+ * const claim = wallet.deriveClaimKey(payment.ephemeralPublicKey, payment.viewTag)
158
+ * // Use claim.privateKey to sign transactions
159
+ * }
160
+ * ```
161
+ */
162
+ export class PrivacyEthereumWalletAdapter extends EthereumWalletAdapter {
163
+ private stealthKeys: EthereumStealthKeyMaterial | undefined
164
+ private deriveFromWallet: boolean
165
+ private derivationDomain: string
166
+ private privacyContext: PrivacyContext | undefined
167
+
168
+ constructor(config: PrivacyEthereumAdapterConfig = {}) {
169
+ super(config)
170
+
171
+ this.deriveFromWallet = config.deriveFromWallet ?? false
172
+ this.derivationDomain = config.derivationDomain ?? 'sip-protocol.org'
173
+
174
+ // If pre-existing keys provided, validate and store
175
+ if (config.metaAddress) {
176
+ if (!config.spendingPrivateKey || !config.viewingPrivateKey) {
177
+ throw new Error('spendingPrivateKey and viewingPrivateKey required when metaAddress provided')
178
+ }
179
+
180
+ // Parse meta-address if string
181
+ let metaAddress: StealthMetaAddress
182
+ if (typeof config.metaAddress === 'string') {
183
+ const parsed = parseEthereumStealthMetaAddress(config.metaAddress)
184
+ metaAddress = {
185
+ chain: 'ethereum',
186
+ spendingKey: parsed.spendingKey,
187
+ viewingKey: parsed.viewingKey,
188
+ }
189
+ } else if ('spendingKey' in config.metaAddress) {
190
+ // Already StealthMetaAddress format
191
+ metaAddress = config.metaAddress as StealthMetaAddress
192
+ } else {
193
+ throw new Error('Invalid meta-address format')
194
+ }
195
+
196
+ this.stealthKeys = {
197
+ metaAddress,
198
+ spendingPrivateKey: config.spendingPrivateKey,
199
+ viewingPrivateKey: config.viewingPrivateKey,
200
+ encodedMetaAddress: encodeEthereumStealthMetaAddress(metaAddress),
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Initialize privacy features
207
+ *
208
+ * Generates or derives stealth keys. Must be called after connect().
209
+ * If deriveFromWallet is true, will prompt for a signature.
210
+ */
211
+ async initializePrivacy(): Promise<void> {
212
+ this.requireConnected()
213
+
214
+ // If we have a privacy context for this address, restore it
215
+ if (this.privacyContext && this.privacyContext.derivedForAddress === this.address) {
216
+ this.stealthKeys = this.privacyContext.keys
217
+ return
218
+ }
219
+
220
+ if (this.stealthKeys) {
221
+ return // Already initialized
222
+ }
223
+
224
+ if (this.deriveFromWallet) {
225
+ await this.deriveStealthKeysFromWallet()
226
+ } else {
227
+ this.generateRandomStealthKeys()
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Check if privacy is initialized
233
+ */
234
+ isPrivacyInitialized(): boolean {
235
+ return !!this.stealthKeys
236
+ }
237
+
238
+ /**
239
+ * Get the stealth meta-address
240
+ *
241
+ * Share this with senders to receive private payments.
242
+ *
243
+ * @throws If privacy not initialized
244
+ */
245
+ getMetaAddress(): StealthMetaAddress {
246
+ if (!this.stealthKeys) {
247
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
248
+ }
249
+ return this.stealthKeys.metaAddress
250
+ }
251
+
252
+ /**
253
+ * Get the encoded EIP-5564 meta-address string
254
+ *
255
+ * Format: st:eth:0x<spendingKey><viewingKey>
256
+ *
257
+ * @throws If privacy not initialized
258
+ */
259
+ getEncodedMetaAddress(): string {
260
+ if (!this.stealthKeys) {
261
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
262
+ }
263
+ return this.stealthKeys.encodedMetaAddress
264
+ }
265
+
266
+ /**
267
+ * Get the viewing private key
268
+ *
269
+ * Used for scanning incoming payments.
270
+ * Can be shared with auditors for compliance.
271
+ *
272
+ * @throws If privacy not initialized
273
+ */
274
+ getViewingPrivateKey(): HexString {
275
+ if (!this.stealthKeys) {
276
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
277
+ }
278
+ return this.stealthKeys.viewingPrivateKey
279
+ }
280
+
281
+ /**
282
+ * Get the spending private key
283
+ *
284
+ * SENSITIVE: Required for deriving stealth private keys.
285
+ * Keep secure and never share.
286
+ *
287
+ * @throws If privacy not initialized
288
+ */
289
+ getSpendingPrivateKey(): HexString {
290
+ if (!this.stealthKeys) {
291
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
292
+ }
293
+ return this.stealthKeys.spendingPrivateKey
294
+ }
295
+
296
+ /**
297
+ * Generate a one-time stealth address for a sender
298
+ *
299
+ * Typically used by the sender side. Recipients share their meta-address,
300
+ * and senders generate unique stealth addresses for each payment.
301
+ *
302
+ * @param recipientMetaAddress - Recipient's stealth meta-address
303
+ * @returns Stealth address details for the transaction
304
+ */
305
+ generateStealthAddressFor(recipientMetaAddress: StealthMetaAddress | string): {
306
+ stealthAddress: StealthAddress
307
+ ethAddress: HexString
308
+ sharedSecret: HexString
309
+ } {
310
+ const { stealthAddress, sharedSecret } = generateEthereumStealthAddress(recipientMetaAddress)
311
+ const ethAddress = stealthPublicKeyToEthAddress(stealthAddress.address)
312
+
313
+ return {
314
+ stealthAddress,
315
+ ethAddress,
316
+ sharedSecret,
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Scan announcements to find payments belonging to us
322
+ *
323
+ * Uses the viewing key to check if stealth addresses belong to this wallet.
324
+ * First checks view tags for fast filtering, then verifies ownership.
325
+ *
326
+ * @param announcements - Array of stealth address announcements to scan
327
+ * @returns Array of payments with ownership status
328
+ */
329
+ scanPayments(announcements: StealthAddress[]): EthereumScannedPayment[] {
330
+ if (!this.stealthKeys) {
331
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
332
+ }
333
+
334
+ const results: EthereumScannedPayment[] = []
335
+
336
+ for (const announcement of announcements) {
337
+ try {
338
+ const isOwned = checkEthereumStealthAddress(
339
+ announcement,
340
+ this.stealthKeys.metaAddress.spendingKey,
341
+ this.stealthKeys.viewingPrivateKey,
342
+ )
343
+
344
+ let ethAddress: HexString
345
+ try {
346
+ ethAddress = stealthPublicKeyToEthAddress(announcement.address)
347
+ } catch {
348
+ ethAddress = '0x0000000000000000000000000000000000000000' as HexString
349
+ }
350
+
351
+ results.push({
352
+ stealthAddress: announcement.address,
353
+ ephemeralPublicKey: announcement.ephemeralPublicKey,
354
+ viewTag: announcement.viewTag,
355
+ isOwned,
356
+ ethAddress,
357
+ })
358
+ } catch {
359
+ // Invalid announcement, skip
360
+ results.push({
361
+ stealthAddress: announcement.address,
362
+ ephemeralPublicKey: announcement.ephemeralPublicKey,
363
+ viewTag: announcement.viewTag,
364
+ isOwned: false,
365
+ ethAddress: '0x0000000000000000000000000000000000000000' as HexString,
366
+ })
367
+ }
368
+ }
369
+
370
+ return results
371
+ }
372
+
373
+ /**
374
+ * Fast scan using view tags only
375
+ *
376
+ * Pre-filters announcements by view tag before full verification.
377
+ * Much faster for large announcement sets.
378
+ *
379
+ * @param announcements - Announcements with view tags
380
+ * @returns Potentially matching announcements
381
+ */
382
+ fastScanByViewTag(announcements: StealthAddress[]): StealthAddress[] {
383
+ if (!this.stealthKeys) {
384
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
385
+ }
386
+
387
+ // Get viewing private key bytes
388
+ const viewingKeyBytes = hexToBytes(this.stealthKeys.viewingPrivateKey.slice(2))
389
+
390
+ return announcements.filter(ann => {
391
+ try {
392
+ const ephemeralBytes = hexToBytes(ann.ephemeralPublicKey.slice(2))
393
+
394
+ // Compute shared secret point
395
+ const ephemeralPoint = secp256k1.ProjectivePoint.fromHex(ephemeralBytes)
396
+ const sharedPoint = ephemeralPoint.multiply(bytesToBigInt(viewingKeyBytes))
397
+ const sharedSecretBytes = sharedPoint.toRawBytes(true)
398
+
399
+ // Hash shared secret to get view tag
400
+ const hash = keccak_256(sharedSecretBytes)
401
+ const expectedViewTag = hash[0]
402
+
403
+ return ann.viewTag === expectedViewTag
404
+ } catch {
405
+ return false
406
+ }
407
+ })
408
+ }
409
+
410
+ /**
411
+ * Derive the private key for spending from a stealth address
412
+ *
413
+ * @param ephemeralPublicKey - Ephemeral public key from the announcement
414
+ * @param viewTag - View tag from the announcement
415
+ * @returns Claim key material for spending
416
+ */
417
+ deriveClaimKey(
418
+ ephemeralPublicKey: HexString,
419
+ viewTag: number
420
+ ): EthereumClaimResult {
421
+ if (!this.stealthKeys) {
422
+ throw new Error('Privacy not initialized. Call initializePrivacy() first.')
423
+ }
424
+
425
+ // Create StealthAddress for derivation
426
+ const stealthAddr: StealthAddress = {
427
+ address: '0x' + '00'.repeat(33) as HexString, // Will be computed
428
+ ephemeralPublicKey,
429
+ viewTag,
430
+ }
431
+
432
+ // Derive the private key using our crypto module
433
+ const recovery = deriveEthereumStealthPrivateKey(
434
+ stealthAddr,
435
+ this.stealthKeys.spendingPrivateKey,
436
+ this.stealthKeys.viewingPrivateKey,
437
+ )
438
+
439
+ return {
440
+ privateKey: recovery.privateKey,
441
+ publicKey: recovery.stealthAddress,
442
+ ethAddress: recovery.ethAddress,
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Sign EIP-712 typed data for permit or other structured data
448
+ *
449
+ * Convenience method that uses the wallet's signTypedData.
450
+ *
451
+ * @param typedData - EIP-712 typed data
452
+ * @returns Signature
453
+ */
454
+ async signPermitData(typedData: EIP712TypedData): Promise<{
455
+ v: number
456
+ r: HexString
457
+ s: HexString
458
+ }> {
459
+ const signature = await this.signTypedData(typedData)
460
+
461
+ // Parse signature into v, r, s
462
+ const sig = signature.signature.slice(2) // Remove 0x
463
+ const r = `0x${sig.slice(0, 64)}` as HexString
464
+ const s = `0x${sig.slice(64, 128)}` as HexString
465
+ const v = parseInt(sig.slice(128, 130), 16)
466
+
467
+ return { v, r, s }
468
+ }
469
+
470
+ /**
471
+ * Export stealth keys for backup
472
+ *
473
+ * Returns all key material needed to restore privacy functionality.
474
+ * SENSITIVE: Store securely and never expose.
475
+ */
476
+ exportStealthKeys(): EthereumStealthKeyMaterial | undefined {
477
+ return this.stealthKeys ? { ...this.stealthKeys } : undefined
478
+ }
479
+
480
+ /**
481
+ * Import stealth keys from backup
482
+ *
483
+ * @param keys - Previously exported stealth keys
484
+ */
485
+ importStealthKeys(keys: EthereumStealthKeyMaterial): void {
486
+ this.stealthKeys = { ...keys }
487
+ }
488
+
489
+ /**
490
+ * Get privacy context for persistence
491
+ *
492
+ * Use this to save privacy state across reconnections.
493
+ */
494
+ getPrivacyContext(): PrivacyContext | undefined {
495
+ if (!this.stealthKeys) {
496
+ return undefined
497
+ }
498
+
499
+ return {
500
+ keys: { ...this.stealthKeys },
501
+ derivedFromWallet: this.deriveFromWallet,
502
+ derivationDomain: this.deriveFromWallet ? this.derivationDomain : undefined,
503
+ derivedForAddress: this.deriveFromWallet ? this.address : undefined,
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Restore privacy context from saved state
509
+ *
510
+ * @param context - Previously saved privacy context
511
+ */
512
+ setPrivacyContext(context: PrivacyContext): void {
513
+ this.privacyContext = context
514
+ if (this.address === context.derivedForAddress || !context.derivedFromWallet) {
515
+ this.stealthKeys = context.keys
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Clear privacy state
521
+ *
522
+ * Removes all stealth keys from memory.
523
+ */
524
+ clearPrivacy(): void {
525
+ this.stealthKeys = undefined
526
+ this.privacyContext = undefined
527
+ }
528
+
529
+ /**
530
+ * Generate random stealth keys
531
+ */
532
+ private generateRandomStealthKeys(): void {
533
+ const { metaAddress, encoded, spendingPrivateKey, viewingPrivateKey } =
534
+ generateEthereumStealthMetaAddress()
535
+
536
+ this.stealthKeys = {
537
+ metaAddress: {
538
+ chain: 'ethereum',
539
+ spendingKey: metaAddress.spendingKey,
540
+ viewingKey: metaAddress.viewingKey,
541
+ },
542
+ spendingPrivateKey,
543
+ viewingPrivateKey,
544
+ encodedMetaAddress: encoded,
545
+ }
546
+ }
547
+
548
+ /**
549
+ * Derive stealth keys deterministically from wallet signature
550
+ *
551
+ * Uses wallet signature over a domain-specific message to derive
552
+ * deterministic stealth keys. Same wallet + domain = same keys.
553
+ */
554
+ private async deriveStealthKeysFromWallet(): Promise<void> {
555
+ const message = `SIP Protocol Stealth Key Derivation\n` +
556
+ `Domain: ${this.derivationDomain}\n` +
557
+ `Address: ${this.address}\n` +
558
+ `Chain: ethereum\n` +
559
+ `Version: 1`
560
+
561
+ // Sign the derivation message
562
+ const messageBytes = new TextEncoder().encode(message)
563
+ const signature = await this.signMessage(messageBytes)
564
+
565
+ // Use signature as seed for key derivation
566
+ const signatureBytes = hexToBytes(signature.signature.slice(2))
567
+
568
+ // Derive spending key: hash(sig || "spending")
569
+ const spendingInput = new Uint8Array(signatureBytes.length + 8)
570
+ spendingInput.set(signatureBytes)
571
+ spendingInput.set(new TextEncoder().encode('spending'), signatureBytes.length)
572
+ const spendingHash = sha256(spendingInput)
573
+
574
+ // Ensure spending key is valid for secp256k1
575
+ const spendingPrivateKey = ensureValidSecp256k1Key(spendingHash)
576
+
577
+ // Derive viewing key: hash(sig || "viewing")
578
+ const viewingInput = new Uint8Array(signatureBytes.length + 7)
579
+ viewingInput.set(signatureBytes)
580
+ viewingInput.set(new TextEncoder().encode('viewing'), signatureBytes.length)
581
+ const viewingHash = sha256(viewingInput)
582
+
583
+ // Ensure viewing key is valid for secp256k1
584
+ const viewingPrivateKey = ensureValidSecp256k1Key(viewingHash)
585
+
586
+ // Compute public keys (compressed)
587
+ const spendingKey = secp256k1.getPublicKey(spendingPrivateKey, true)
588
+ const viewingKey = secp256k1.getPublicKey(viewingPrivateKey, true)
589
+
590
+ const spendingKeyHex = `0x${bytesToHex(spendingKey)}` as HexString
591
+ const viewingKeyHex = `0x${bytesToHex(viewingKey)}` as HexString
592
+
593
+ const metaAddress: StealthMetaAddress = {
594
+ chain: 'ethereum',
595
+ spendingKey: spendingKeyHex,
596
+ viewingKey: viewingKeyHex,
597
+ }
598
+
599
+ this.stealthKeys = {
600
+ metaAddress,
601
+ spendingPrivateKey: `0x${bytesToHex(spendingPrivateKey)}` as HexString,
602
+ viewingPrivateKey: `0x${bytesToHex(viewingPrivateKey)}` as HexString,
603
+ encodedMetaAddress: encodeEthereumStealthMetaAddress(metaAddress),
604
+ }
605
+
606
+ // Store privacy context
607
+ this.privacyContext = {
608
+ keys: this.stealthKeys,
609
+ derivedFromWallet: true,
610
+ derivationDomain: this.derivationDomain,
611
+ derivedForAddress: this.address,
612
+ }
613
+ }
614
+ }
615
+
616
+ // ─── Utilities ──────────────────────────────────────────────────────────────
617
+
618
+ /** secp256k1 curve order */
619
+ const SECP256K1_ORDER = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141')
620
+
621
+ /**
622
+ * Convert hex string to bytes
623
+ */
624
+ function hexToBytes(hex: string): Uint8Array {
625
+ const bytes = new Uint8Array(hex.length / 2)
626
+ for (let i = 0; i < hex.length; i += 2) {
627
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
628
+ }
629
+ return bytes
630
+ }
631
+
632
+ /**
633
+ * Convert bytes to hex string
634
+ */
635
+ function bytesToHex(bytes: Uint8Array): string {
636
+ return Array.from(bytes)
637
+ .map(b => b.toString(16).padStart(2, '0'))
638
+ .join('')
639
+ }
640
+
641
+ /**
642
+ * Convert bytes to BigInt (big-endian)
643
+ */
644
+ function bytesToBigInt(bytes: Uint8Array): bigint {
645
+ let result = 0n
646
+ for (let i = 0; i < bytes.length; i++) {
647
+ result = (result << 8n) | BigInt(bytes[i])
648
+ }
649
+ return result
650
+ }
651
+
652
+ /**
653
+ * Ensure key is valid for secp256k1 (1 < key < curve order)
654
+ */
655
+ function ensureValidSecp256k1Key(hash: Uint8Array): Uint8Array {
656
+ let key = bytesToBigInt(hash)
657
+
658
+ // Reduce modulo curve order and ensure non-zero
659
+ key = key % SECP256K1_ORDER
660
+ if (key === 0n) {
661
+ key = 1n
662
+ }
663
+
664
+ // Convert back to bytes
665
+ const result = new Uint8Array(32)
666
+ for (let i = 31; i >= 0; i--) {
667
+ result[i] = Number(key & 0xffn)
668
+ key >>= 8n
669
+ }
670
+
671
+ return result
672
+ }
673
+
674
+ // ─── Factory ────────────────────────────────────────────────────────────────
675
+
676
+ /**
677
+ * Create a privacy-enabled Ethereum wallet adapter
678
+ *
679
+ * @example Random keys
680
+ * ```typescript
681
+ * const wallet = createPrivacyEthereumAdapter({ wallet: 'metamask' })
682
+ * await wallet.connect()
683
+ * await wallet.initializePrivacy()
684
+ * ```
685
+ *
686
+ * @example Deterministic keys from wallet
687
+ * ```typescript
688
+ * const wallet = createPrivacyEthereumAdapter({
689
+ * wallet: 'metamask',
690
+ * deriveFromWallet: true,
691
+ * })
692
+ * await wallet.connect()
693
+ * await wallet.initializePrivacy() // Will prompt for signature
694
+ * ```
695
+ */
696
+ export function createPrivacyEthereumAdapter(
697
+ config: PrivacyEthereumAdapterConfig = {}
698
+ ): PrivacyEthereumWalletAdapter {
699
+ return new PrivacyEthereumWalletAdapter(config)
700
+ }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { HexString } from '@sip-protocol/types'
9
+ import { ETH_RPC_ENDPOINTS } from '../../config/endpoints'
9
10
 
10
11
  // ============================================================================
11
12
  // EIP-1193 Provider Types
@@ -359,6 +360,7 @@ export function getDefaultRpcEndpoint(chainId: number): string {
359
360
  case EthereumChainId.BASE:
360
361
  return 'https://mainnet.base.org'
361
362
  default:
362
- return 'http://localhost:8545'
363
+ // Configurable via ETH_LOCALNET_RPC environment variable
364
+ return ETH_RPC_ENDPOINTS.localnet
363
365
  }
364
366
  }