@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,455 @@
1
+ /**
2
+ * Ethereum Viewing Key Management (EIP-5564)
3
+ *
4
+ * Viewing keys enable selective disclosure for compliance and auditing.
5
+ * The viewing key holder can scan for incoming payments but cannot spend funds.
6
+ *
7
+ * ## Use Cases
8
+ *
9
+ * 1. **Self-Scanning**: Recipient uses viewing key to find incoming payments
10
+ * 2. **Audit/Compliance**: Share viewing key with auditors for transaction visibility
11
+ * 3. **Watch-Only Wallets**: Monitor stealth addresses without spending capability
12
+ *
13
+ * ## Security Model
14
+ *
15
+ * - Viewing key = can see incoming payments, cannot spend
16
+ * - Spending key = required to claim/spend funds
17
+ * - Both keys derive from the same meta-address
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+
22
+ import { secp256k1 } from '@noble/curves/secp256k1'
23
+ import { sha256 } from '@noble/hashes/sha256'
24
+ import { bytesToHex, randomBytes } from '@noble/hashes/utils'
25
+ import type { HexString, StealthMetaAddress } from '@sip-protocol/types'
26
+ import { ValidationError } from '../../errors'
27
+ import { isValidHexLength, isValidPrivateKey } from '../../validation'
28
+ import type {
29
+ EthereumViewingKeyExport,
30
+ EthereumViewingKeyPair,
31
+ EthereumPrivacyLevel,
32
+ } from './types'
33
+ import type { EthereumNetwork } from './constants'
34
+
35
+ // ─── Types ────────────────────────────────────────────────────────────────────
36
+
37
+ /**
38
+ * Viewing key disclosure scope
39
+ */
40
+ export type ViewingKeyScope =
41
+ | 'all' // Can see all transactions
42
+ | 'incoming' // Can see incoming only
43
+ | 'range' // Can see transactions in block range
44
+
45
+ /**
46
+ * Viewing key permissions
47
+ */
48
+ export interface ViewingKeyPermissions {
49
+ /** Can view incoming payments */
50
+ canViewIncoming: boolean
51
+ /** Can view outgoing payments (requires additional data) */
52
+ canViewOutgoing: boolean
53
+ /** Can view transaction amounts (vs just detecting payments) */
54
+ canViewAmounts: boolean
55
+ /** Block range restriction (if scope is 'range') */
56
+ blockRange?: {
57
+ from: number
58
+ to: number
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Shared viewing key for auditors
64
+ */
65
+ export interface SharedViewingKey {
66
+ /** The viewing public key */
67
+ viewingPublicKey: HexString
68
+ /** The spending public key (for address verification) */
69
+ spendingPublicKey: HexString
70
+ /** Permissions granted */
71
+ permissions: ViewingKeyPermissions
72
+ /** Optional label */
73
+ label?: string
74
+ /** Expiration timestamp (ISO 8601) */
75
+ expiresAt?: string
76
+ /** Signature proving ownership (optional) */
77
+ signature?: HexString
78
+ }
79
+
80
+ // ─── Key Generation ─────────────────────────────────────────────────────────
81
+
82
+ /**
83
+ * Generate a new viewing keypair
84
+ *
85
+ * @param spendingPublicKey - Associated spending public key
86
+ * @param label - Optional label
87
+ * @returns Viewing keypair
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const viewingKey = generateViewingKeyPair(
92
+ * metaAddress.spendingKey,
93
+ * 'Main Wallet'
94
+ * )
95
+ * ```
96
+ */
97
+ export function generateViewingKeyPair(
98
+ spendingPublicKey: HexString,
99
+ label?: string
100
+ ): EthereumViewingKeyPair {
101
+ // Generate random viewing private key
102
+ const viewingPrivateKey = randomBytes(32)
103
+ const viewingPublicKey = secp256k1.getPublicKey(viewingPrivateKey, true)
104
+
105
+ return {
106
+ publicKey: `0x${bytesToHex(viewingPublicKey)}` as HexString,
107
+ privateKey: `0x${bytesToHex(viewingPrivateKey)}` as HexString,
108
+ spendingPublicKey,
109
+ label,
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Derive viewing public key from private key
115
+ *
116
+ * @param viewingPrivateKey - The viewing private key
117
+ * @returns Viewing public key
118
+ */
119
+ export function deriveViewingPublicKey(viewingPrivateKey: HexString): HexString {
120
+ if (!isValidPrivateKey(viewingPrivateKey)) {
121
+ throw new ValidationError(
122
+ 'must be a valid 32-byte hex string',
123
+ 'viewingPrivateKey'
124
+ )
125
+ }
126
+
127
+ const privateKeyBytes = hexToBytes(viewingPrivateKey.slice(2))
128
+ const publicKey = secp256k1.getPublicKey(privateKeyBytes, true)
129
+
130
+ return `0x${bytesToHex(publicKey)}` as HexString
131
+ }
132
+
133
+ // ─── Key Export/Import ──────────────────────────────────────────────────────
134
+
135
+ /**
136
+ * Export a viewing key for sharing with auditors
137
+ *
138
+ * @param viewingKeyPair - The viewing keypair (only public parts exported)
139
+ * @param network - The Ethereum network
140
+ * @param expiresAt - Optional expiration timestamp
141
+ * @returns Exportable viewing key object
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const exported = exportViewingKey(viewingKey, 'mainnet')
146
+ * // Share 'exported' with auditor (contains no private keys)
147
+ * ```
148
+ */
149
+ export function exportViewingKey(
150
+ viewingKeyPair: EthereumViewingKeyPair,
151
+ network: EthereumNetwork,
152
+ expiresAt?: Date
153
+ ): EthereumViewingKeyExport {
154
+ return {
155
+ version: 1,
156
+ chain: 'ethereum',
157
+ network,
158
+ viewingPublicKey: viewingKeyPair.publicKey,
159
+ spendingPublicKey: viewingKeyPair.spendingPublicKey,
160
+ label: viewingKeyPair.label,
161
+ createdAt: new Date().toISOString(),
162
+ expiresAt: expiresAt?.toISOString(),
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Import/parse an exported viewing key
168
+ *
169
+ * @param exported - The exported viewing key JSON
170
+ * @returns Parsed viewing key export
171
+ */
172
+ export function importViewingKey(
173
+ exported: string | EthereumViewingKeyExport
174
+ ): EthereumViewingKeyExport {
175
+ const data =
176
+ typeof exported === 'string'
177
+ ? (JSON.parse(exported) as EthereumViewingKeyExport)
178
+ : exported
179
+
180
+ // Validate structure
181
+ if (data.version !== 1) {
182
+ throw new ValidationError(
183
+ `unsupported viewing key version: ${data.version}`,
184
+ 'version'
185
+ )
186
+ }
187
+
188
+ if (data.chain !== 'ethereum') {
189
+ throw new ValidationError(
190
+ `invalid chain: ${data.chain}, expected 'ethereum'`,
191
+ 'chain'
192
+ )
193
+ }
194
+
195
+ if (!isValidHexLength(data.viewingPublicKey, 33)) {
196
+ throw new ValidationError(
197
+ 'viewingPublicKey must be a valid 33-byte hex string',
198
+ 'viewingPublicKey'
199
+ )
200
+ }
201
+
202
+ if (!isValidHexLength(data.spendingPublicKey, 33)) {
203
+ throw new ValidationError(
204
+ 'spendingPublicKey must be a valid 33-byte hex string',
205
+ 'spendingPublicKey'
206
+ )
207
+ }
208
+
209
+ return data
210
+ }
211
+
212
+ /**
213
+ * Serialize a viewing key export to JSON string
214
+ *
215
+ * @param exportData - The viewing key export
216
+ * @returns JSON string
217
+ */
218
+ export function serializeViewingKey(exportData: EthereumViewingKeyExport): string {
219
+ return JSON.stringify(exportData, null, 2)
220
+ }
221
+
222
+ // ─── Key Verification ───────────────────────────────────────────────────────
223
+
224
+ /**
225
+ * Verify that a viewing key matches a meta-address
226
+ *
227
+ * @param viewingPublicKey - The viewing public key to verify
228
+ * @param metaAddress - The meta-address to check against
229
+ * @returns True if the viewing key matches
230
+ */
231
+ export function verifyViewingKeyMatches(
232
+ viewingPublicKey: HexString,
233
+ metaAddress: StealthMetaAddress
234
+ ): boolean {
235
+ // Normalize both keys (remove 0x prefix, lowercase)
236
+ const normalizedViewing = viewingPublicKey.slice(2).toLowerCase()
237
+ const normalizedMeta = metaAddress.viewingKey.slice(2).toLowerCase()
238
+
239
+ return normalizedViewing === normalizedMeta
240
+ }
241
+
242
+ /**
243
+ * Check if a viewing key export has expired
244
+ *
245
+ * @param exportData - The viewing key export
246
+ * @returns True if expired
247
+ */
248
+ export function isViewingKeyExpired(exportData: EthereumViewingKeyExport): boolean {
249
+ if (!exportData.expiresAt) {
250
+ return false
251
+ }
252
+
253
+ const expiresAt = new Date(exportData.expiresAt)
254
+ return expiresAt < new Date()
255
+ }
256
+
257
+ // ─── Shared Key Creation ────────────────────────────────────────────────────
258
+
259
+ /**
260
+ * Create a shared viewing key with specific permissions
261
+ *
262
+ * @param viewingKeyPair - The full viewing keypair
263
+ * @param permissions - Permissions to grant
264
+ * @param expiresAt - Optional expiration
265
+ * @returns Shared viewing key for auditor
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * // Create a read-only viewing key for auditor
270
+ * const sharedKey = createSharedViewingKey(viewingKey, {
271
+ * canViewIncoming: true,
272
+ * canViewOutgoing: false,
273
+ * canViewAmounts: true,
274
+ * })
275
+ * ```
276
+ */
277
+ export function createSharedViewingKey(
278
+ viewingKeyPair: EthereumViewingKeyPair,
279
+ permissions: ViewingKeyPermissions,
280
+ expiresAt?: Date
281
+ ): SharedViewingKey {
282
+ return {
283
+ viewingPublicKey: viewingKeyPair.publicKey,
284
+ spendingPublicKey: viewingKeyPair.spendingPublicKey,
285
+ permissions,
286
+ label: viewingKeyPair.label,
287
+ expiresAt: expiresAt?.toISOString(),
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Create a full-access viewing key (for self-scanning)
293
+ *
294
+ * @param viewingKeyPair - The viewing keypair
295
+ * @returns Shared key with full permissions
296
+ */
297
+ export function createFullAccessViewingKey(
298
+ viewingKeyPair: EthereumViewingKeyPair
299
+ ): SharedViewingKey {
300
+ return createSharedViewingKey(viewingKeyPair, {
301
+ canViewIncoming: true,
302
+ canViewOutgoing: true,
303
+ canViewAmounts: true,
304
+ })
305
+ }
306
+
307
+ /**
308
+ * Create a compliance-restricted viewing key
309
+ *
310
+ * @param viewingKeyPair - The viewing keypair
311
+ * @param fromBlock - Start block for visibility
312
+ * @param toBlock - End block for visibility
313
+ * @returns Shared key with block range restriction
314
+ */
315
+ export function createRangeRestrictedViewingKey(
316
+ viewingKeyPair: EthereumViewingKeyPair,
317
+ fromBlock: number,
318
+ toBlock: number
319
+ ): SharedViewingKey {
320
+ return createSharedViewingKey(viewingKeyPair, {
321
+ canViewIncoming: true,
322
+ canViewOutgoing: false,
323
+ canViewAmounts: true,
324
+ blockRange: { from: fromBlock, to: toBlock },
325
+ })
326
+ }
327
+
328
+ // ─── Hash Functions ─────────────────────────────────────────────────────────
329
+
330
+ /**
331
+ * Compute a hash of the viewing key for indexing/lookup
332
+ *
333
+ * @param viewingPublicKey - The viewing public key
334
+ * @returns Hash of the viewing key
335
+ */
336
+ export function hashViewingKey(viewingPublicKey: HexString): HexString {
337
+ const keyBytes = hexToBytes(viewingPublicKey.slice(2))
338
+ const hash = sha256(keyBytes)
339
+ return `0x${bytesToHex(hash)}` as HexString
340
+ }
341
+
342
+ /**
343
+ * Compute the viewing key hash used in on-chain registry
344
+ *
345
+ * @param viewingPublicKey - The viewing public key
346
+ * @param spendingPublicKey - The spending public key
347
+ * @returns Combined hash for registry lookup
348
+ */
349
+ export function computeRegistryHash(
350
+ viewingPublicKey: HexString,
351
+ spendingPublicKey: HexString
352
+ ): HexString {
353
+ const viewingBytes = hexToBytes(viewingPublicKey.slice(2))
354
+ const spendingBytes = hexToBytes(spendingPublicKey.slice(2))
355
+
356
+ // Concatenate and hash
357
+ const combined = new Uint8Array(viewingBytes.length + spendingBytes.length)
358
+ combined.set(viewingBytes, 0)
359
+ combined.set(spendingBytes, viewingBytes.length)
360
+
361
+ const hash = sha256(combined)
362
+ return `0x${bytesToHex(hash)}` as HexString
363
+ }
364
+
365
+ // ─── Privacy Level Helpers ──────────────────────────────────────────────────
366
+
367
+ /**
368
+ * Get the appropriate viewing key usage based on privacy level
369
+ *
370
+ * @param privacyLevel - The privacy level
371
+ * @returns Description of viewing key usage
372
+ */
373
+ export function getViewingKeyUsage(
374
+ privacyLevel: EthereumPrivacyLevel
375
+ ): {
376
+ required: boolean
377
+ shareable: boolean
378
+ description: string
379
+ } {
380
+ switch (privacyLevel) {
381
+ case 'transparent':
382
+ return {
383
+ required: false,
384
+ shareable: false,
385
+ description: 'No privacy - all transaction details are public',
386
+ }
387
+ case 'shielded':
388
+ return {
389
+ required: true,
390
+ shareable: false,
391
+ description: 'Full privacy - viewing key for self-scanning only',
392
+ }
393
+ case 'compliant':
394
+ return {
395
+ required: true,
396
+ shareable: true,
397
+ description: 'Privacy with compliance - viewing key can be shared with auditors',
398
+ }
399
+ }
400
+ }
401
+
402
+ // ─── Utility Functions ──────────────────────────────────────────────────────
403
+
404
+ /**
405
+ * Extract viewing key components from a meta-address
406
+ *
407
+ * @param metaAddress - The stealth meta-address
408
+ * @returns Viewing and spending public keys
409
+ */
410
+ export function extractViewingComponents(metaAddress: StealthMetaAddress): {
411
+ viewingPublicKey: HexString
412
+ spendingPublicKey: HexString
413
+ } {
414
+ return {
415
+ viewingPublicKey: metaAddress.viewingKey,
416
+ spendingPublicKey: metaAddress.spendingKey,
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Validate viewing key permissions
422
+ *
423
+ * @param permissions - The permissions to validate
424
+ * @returns True if valid
425
+ */
426
+ export function validatePermissions(permissions: ViewingKeyPermissions): boolean {
427
+ if (permissions.blockRange) {
428
+ if (permissions.blockRange.from < 0 || permissions.blockRange.to < 0) {
429
+ return false
430
+ }
431
+ if (permissions.blockRange.from > permissions.blockRange.to) {
432
+ return false
433
+ }
434
+ }
435
+
436
+ // At least one permission should be granted
437
+ return (
438
+ permissions.canViewIncoming ||
439
+ permissions.canViewOutgoing ||
440
+ permissions.canViewAmounts
441
+ )
442
+ }
443
+
444
+ // ─── Helper Functions ─────────────────────────────────────────────────────────
445
+
446
+ /**
447
+ * Convert hex string to bytes
448
+ */
449
+ function hexToBytes(hex: string): Uint8Array {
450
+ const bytes = new Uint8Array(hex.length / 2)
451
+ for (let i = 0; i < bytes.length; i++) {
452
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16)
453
+ }
454
+ return bytes
455
+ }