@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,790 @@
1
+ /**
2
+ * Helius Webhook Handler
3
+ *
4
+ * Real-time stealth payment detection using Helius webhooks.
5
+ * Push-based notifications instead of polling for efficient scanning.
6
+ *
7
+ * @see https://docs.helius.dev/webhooks-and-websockets/webhooks
8
+ *
9
+ * @example Server setup (Express.js)
10
+ * ```typescript
11
+ * import express from 'express'
12
+ * import { createWebhookHandler } from '@sip-protocol/sdk'
13
+ *
14
+ * const app = express()
15
+ * app.use(express.json())
16
+ *
17
+ * const handler = createWebhookHandler({
18
+ * viewingPrivateKey: '0x...',
19
+ * spendingPublicKey: '0x...',
20
+ * onPaymentFound: (payment) => {
21
+ * console.log('Found payment!', payment)
22
+ * // Notify user, update database, etc.
23
+ * },
24
+ * })
25
+ *
26
+ * app.post('/webhook/helius', async (req, res) => {
27
+ * await handler(req.body)
28
+ * res.status(200).send('OK')
29
+ * })
30
+ * ```
31
+ *
32
+ * @example Helius webhook configuration
33
+ * ```
34
+ * Webhook URL: https://your-server.com/webhook/helius
35
+ * Transaction Type: Any (or TRANSFER for token transfers)
36
+ * Account Addresses: [MEMO_PROGRAM_ID] for memo filtering
37
+ * Webhook Type: raw (to get full transaction data)
38
+ * ```
39
+ */
40
+
41
+ import type { HexString } from '@sip-protocol/types'
42
+ import {
43
+ checkEd25519StealthAddress,
44
+ solanaAddressToEd25519PublicKey,
45
+ } from '../../../stealth'
46
+ import type { StealthAddress } from '@sip-protocol/types'
47
+ import { parseAnnouncement } from '../types'
48
+ import type { SolanaScanResult } from '../types'
49
+ import { SIP_MEMO_PREFIX } from '../constants'
50
+ import { getTokenSymbol, parseTokenTransferFromBalances } from '../utils'
51
+ import { ValidationError, SecurityError } from '../../../errors'
52
+ import { hmac } from '@noble/hashes/hmac'
53
+ import { sha256 } from '@noble/hashes/sha256'
54
+ import { bytesToHex } from '@noble/hashes/utils'
55
+
56
+ /**
57
+ * Helius raw webhook payload for a transaction
58
+ *
59
+ * @see https://docs.helius.dev/webhooks-and-websockets/webhooks
60
+ */
61
+ export interface HeliusWebhookTransaction {
62
+ /** Block timestamp (Unix seconds) */
63
+ blockTime: number
64
+ /** Position within block */
65
+ indexWithinBlock?: number
66
+ /** Transaction metadata */
67
+ meta: {
68
+ /** Error if transaction failed */
69
+ err: unknown | null
70
+ /** Transaction fee in lamports */
71
+ fee: number
72
+ /** Inner instructions (CPI calls) */
73
+ innerInstructions: Array<{
74
+ index: number
75
+ instructions: Array<{
76
+ accounts: number[]
77
+ data: string
78
+ programIdIndex: number
79
+ }>
80
+ }>
81
+ /** Loaded address tables */
82
+ loadedAddresses?: {
83
+ readonly: string[]
84
+ writable: string[]
85
+ }
86
+ /** Program log messages */
87
+ logMessages: string[]
88
+ /** Post-transaction lamport balances */
89
+ postBalances: number[]
90
+ /** Post-transaction token balances */
91
+ postTokenBalances: Array<{
92
+ accountIndex: number
93
+ mint: string
94
+ owner?: string
95
+ programId?: string
96
+ uiTokenAmount: {
97
+ amount: string
98
+ decimals: number
99
+ uiAmount: number | null
100
+ uiAmountString: string
101
+ }
102
+ }>
103
+ /** Pre-transaction lamport balances */
104
+ preBalances: number[]
105
+ /** Pre-transaction token balances */
106
+ preTokenBalances: Array<{
107
+ accountIndex: number
108
+ mint: string
109
+ owner?: string
110
+ programId?: string
111
+ uiTokenAmount: {
112
+ amount: string
113
+ decimals: number
114
+ uiAmount: number | null
115
+ uiAmountString: string
116
+ }
117
+ }>
118
+ /** Rewards */
119
+ rewards: unknown[]
120
+ }
121
+ /** Slot number */
122
+ slot: number
123
+ /** Transaction data */
124
+ transaction: {
125
+ /** Transaction message */
126
+ message: {
127
+ /** Account keys involved */
128
+ accountKeys: string[]
129
+ /** Compiled instructions */
130
+ instructions: Array<{
131
+ accounts: number[]
132
+ data: string
133
+ programIdIndex: number
134
+ }>
135
+ /** Recent blockhash */
136
+ recentBlockhash: string
137
+ }
138
+ /** Transaction signatures */
139
+ signatures: string[]
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Helius enhanced webhook payload
145
+ *
146
+ * Enhanced webhooks provide parsed/decoded transaction data.
147
+ */
148
+ export interface HeliusEnhancedTransaction {
149
+ /** Human-readable description */
150
+ description: string
151
+ /** Transaction type (TRANSFER, NFT_SALE, etc.) */
152
+ type: string
153
+ /** Source wallet/program */
154
+ source: string
155
+ /** Transaction fee in lamports */
156
+ fee: number
157
+ /** Fee payer address */
158
+ feePayer: string
159
+ /** Transaction signature */
160
+ signature: string
161
+ /** Slot number */
162
+ slot: number
163
+ /** Block timestamp */
164
+ timestamp: number
165
+ /** Native SOL transfers */
166
+ nativeTransfers: Array<{
167
+ fromUserAccount: string
168
+ toUserAccount: string
169
+ amount: number
170
+ }>
171
+ /** Token transfers */
172
+ tokenTransfers: Array<{
173
+ fromUserAccount: string
174
+ toUserAccount: string
175
+ fromTokenAccount: string
176
+ toTokenAccount: string
177
+ tokenAmount: number
178
+ mint: string
179
+ tokenStandard: string
180
+ }>
181
+ /** Account data changes */
182
+ accountData: Array<{
183
+ account: string
184
+ nativeBalanceChange: number
185
+ tokenBalanceChanges: Array<{
186
+ userAccount: string
187
+ tokenAccount: string
188
+ mint: string
189
+ rawTokenAmount: {
190
+ tokenAmount: string
191
+ decimals: number
192
+ }
193
+ }>
194
+ }>
195
+ /** Events (NFT sales, etc.) */
196
+ events?: Record<string, unknown>
197
+ }
198
+
199
+ /**
200
+ * Webhook payload (can be single transaction or array)
201
+ */
202
+ export type HeliusWebhookPayload =
203
+ | HeliusWebhookTransaction
204
+ | HeliusWebhookTransaction[]
205
+ | HeliusEnhancedTransaction
206
+ | HeliusEnhancedTransaction[]
207
+
208
+ /**
209
+ * Configuration for the Helius webhook handler
210
+ *
211
+ * @security This config contains sensitive cryptographic keys.
212
+ * Never log, store in plain text, or transmit insecurely.
213
+ */
214
+ export interface WebhookHandlerConfig {
215
+ /**
216
+ * Recipient's viewing private key (hex)
217
+ *
218
+ * @security SENSITIVE - This key enables detection of incoming payments.
219
+ * Store securely (encrypted). Never log or expose in error messages.
220
+ * Use environment variables or secure vault in production.
221
+ */
222
+ viewingPrivateKey: HexString
223
+ /** Recipient's spending public key (hex) */
224
+ spendingPublicKey: HexString
225
+ /**
226
+ * Callback when a payment is found
227
+ *
228
+ * @param payment - The detected payment details
229
+ */
230
+ onPaymentFound: (payment: SolanaScanResult) => void | Promise<void>
231
+ /**
232
+ * Optional callback for errors
233
+ *
234
+ * @param error - The error that occurred
235
+ * @param transaction - The transaction that caused the error (if available)
236
+ */
237
+ onError?: (error: Error, transaction?: HeliusWebhookTransaction) => void
238
+ /**
239
+ * Webhook authentication secret (recommended for production)
240
+ *
241
+ * When set, the handler will verify the X-Helius-Signature header
242
+ * using HMAC-SHA256 to ensure webhook payloads are authentic.
243
+ *
244
+ * Get your webhook secret from Helius Dashboard:
245
+ * https://dev.helius.xyz/webhooks
246
+ */
247
+ webhookSecret?: string
248
+ /**
249
+ * Authorization token for additional security
250
+ *
251
+ * When set, the handler will verify the Authorization header
252
+ * matches this token. Use for simple auth in trusted environments.
253
+ */
254
+ authToken?: string
255
+ }
256
+
257
+ /**
258
+ * Webhook request with headers for signature verification
259
+ */
260
+ export interface WebhookRequest {
261
+ /** Raw request body as string (for signature verification) */
262
+ rawBody: string
263
+ /** Parsed payload */
264
+ payload: HeliusWebhookPayload
265
+ /** Request headers */
266
+ headers: {
267
+ /** Helius webhook signature (X-Helius-Signature header) */
268
+ signature?: string
269
+ /** Authorization header */
270
+ authorization?: string
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Result of processing a single webhook transaction
276
+ */
277
+ export interface WebhookProcessResult {
278
+ /** Whether a payment was found for us */
279
+ found: boolean
280
+ /** The payment details (if found) */
281
+ payment?: SolanaScanResult
282
+ /** Transaction signature */
283
+ signature: string
284
+ }
285
+
286
+ /**
287
+ * Verify Helius webhook signature using HMAC-SHA256
288
+ *
289
+ * H-4, H-8 FIX: Implement webhook signature verification
290
+ *
291
+ * @param rawBody - Raw request body as string
292
+ * @param signature - Signature from X-Helius-Signature header
293
+ * @param secret - Webhook secret from Helius dashboard
294
+ * @returns True if signature is valid
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * const isValid = verifyWebhookSignature(
299
+ * req.rawBody,
300
+ * req.headers['x-helius-signature'],
301
+ * process.env.HELIUS_WEBHOOK_SECRET!
302
+ * )
303
+ * if (!isValid) {
304
+ * res.status(401).send('Invalid signature')
305
+ * return
306
+ * }
307
+ * ```
308
+ */
309
+ export function verifyWebhookSignature(
310
+ rawBody: string,
311
+ signature: string | undefined,
312
+ secret: string
313
+ ): boolean {
314
+ if (!signature || !secret) {
315
+ return false
316
+ }
317
+
318
+ try {
319
+ // Compute expected signature using HMAC-SHA256
320
+ const encoder = new TextEncoder()
321
+ const expectedSignature = bytesToHex(
322
+ hmac(sha256, encoder.encode(secret), encoder.encode(rawBody))
323
+ )
324
+
325
+ // Constant-time comparison to prevent timing attacks
326
+ if (signature.length !== expectedSignature.length) {
327
+ return false
328
+ }
329
+
330
+ let result = 0
331
+ for (let i = 0; i < signature.length; i++) {
332
+ result |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i)
333
+ }
334
+ return result === 0
335
+ } catch {
336
+ return false
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Verify authorization token
342
+ *
343
+ * H-7 FIX: Implement authorization verification
344
+ *
345
+ * @param authHeader - Authorization header value
346
+ * @param expectedToken - Expected token
347
+ * @returns True if token matches
348
+ */
349
+ export function verifyAuthToken(
350
+ authHeader: string | undefined,
351
+ expectedToken: string
352
+ ): boolean {
353
+ if (!authHeader || !expectedToken) {
354
+ return false
355
+ }
356
+
357
+ // Support both "Bearer <token>" and raw token formats
358
+ const token = authHeader.startsWith('Bearer ')
359
+ ? authHeader.slice(7)
360
+ : authHeader
361
+
362
+ // Constant-time comparison
363
+ if (token.length !== expectedToken.length) {
364
+ return false
365
+ }
366
+
367
+ let result = 0
368
+ for (let i = 0; i < token.length; i++) {
369
+ result |= token.charCodeAt(i) ^ expectedToken.charCodeAt(i)
370
+ }
371
+ return result === 0
372
+ }
373
+
374
+ /**
375
+ * Handler function type returned by createWebhookHandler
376
+ */
377
+ export interface WebhookHandler {
378
+ /**
379
+ * Process a webhook payload (simple mode - no signature verification)
380
+ */
381
+ (payload: HeliusWebhookPayload): Promise<WebhookProcessResult[]>
382
+
383
+ /**
384
+ * Process a webhook request with full authentication
385
+ *
386
+ * H-4, H-7, H-8 FIX: Supports signature and auth token verification
387
+ */
388
+ processRequest(request: WebhookRequest): Promise<WebhookProcessResult[]>
389
+ }
390
+
391
+ /**
392
+ * Create a webhook handler for processing Helius webhook payloads
393
+ *
394
+ * @param config - Handler configuration
395
+ * @returns Handler function to process webhook payloads
396
+ *
397
+ * @example Simple usage (not recommended for production)
398
+ * ```typescript
399
+ * const handler = createWebhookHandler({
400
+ * viewingPrivateKey: recipientKeys.viewingPrivateKey,
401
+ * spendingPublicKey: recipientKeys.spendingPublicKey,
402
+ * onPaymentFound: async (payment) => {
403
+ * await db.savePayment(payment)
404
+ * },
405
+ * })
406
+ *
407
+ * app.post('/webhook', async (req, res) => {
408
+ * const results = await handler(req.body)
409
+ * res.json({ processed: results.length })
410
+ * })
411
+ * ```
412
+ *
413
+ * @example Production usage with signature verification
414
+ * ```typescript
415
+ * const handler = createWebhookHandler({
416
+ * viewingPrivateKey: recipientKeys.viewingPrivateKey,
417
+ * spendingPublicKey: recipientKeys.spendingPublicKey,
418
+ * webhookSecret: process.env.HELIUS_WEBHOOK_SECRET!,
419
+ * onPaymentFound: async (payment) => {
420
+ * await db.savePayment(payment)
421
+ * },
422
+ * })
423
+ *
424
+ * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
425
+ * try {
426
+ * const results = await handler.processRequest({
427
+ * rawBody: req.body.toString(),
428
+ * payload: JSON.parse(req.body.toString()),
429
+ * headers: {
430
+ * signature: req.headers['x-helius-signature'] as string,
431
+ * authorization: req.headers['authorization'] as string,
432
+ * },
433
+ * })
434
+ * res.json({ processed: results.length })
435
+ * } catch (error) {
436
+ * if (error instanceof SecurityError) {
437
+ * res.status(401).json({ error: error.message })
438
+ * } else {
439
+ * res.status(500).json({ error: 'Internal error' })
440
+ * }
441
+ * }
442
+ * })
443
+ * ```
444
+ */
445
+ export function createWebhookHandler(config: WebhookHandlerConfig): WebhookHandler {
446
+ const {
447
+ viewingPrivateKey,
448
+ spendingPublicKey,
449
+ onPaymentFound,
450
+ onError,
451
+ webhookSecret,
452
+ authToken,
453
+ } = config
454
+
455
+ // Validate required keys
456
+ if (!viewingPrivateKey || !viewingPrivateKey.startsWith('0x')) {
457
+ throw new ValidationError('viewingPrivateKey must be a valid hex string starting with 0x', 'viewingPrivateKey')
458
+ }
459
+ if (!spendingPublicKey || !spendingPublicKey.startsWith('0x')) {
460
+ throw new ValidationError('spendingPublicKey must be a valid hex string starting with 0x', 'spendingPublicKey')
461
+ }
462
+ if (typeof onPaymentFound !== 'function') {
463
+ throw new ValidationError('onPaymentFound callback is required', 'onPaymentFound')
464
+ }
465
+
466
+ // Validate key lengths (ed25519 keys are 32 bytes = 64 hex chars + '0x' prefix)
467
+ if (viewingPrivateKey.length !== 66) {
468
+ throw new ValidationError('viewingPrivateKey must be 32 bytes (64 hex characters)', 'viewingPrivateKey')
469
+ }
470
+ if (spendingPublicKey.length !== 66) {
471
+ throw new ValidationError('spendingPublicKey must be 32 bytes (64 hex characters)', 'spendingPublicKey')
472
+ }
473
+
474
+ /**
475
+ * Process transactions after authentication
476
+ */
477
+ async function processTransactions(
478
+ payload: HeliusWebhookPayload
479
+ ): Promise<WebhookProcessResult[]> {
480
+ // H-4 FIX: Validate payload structure before processing
481
+ if (!payload || (typeof payload !== 'object')) {
482
+ throw new ValidationError('Invalid webhook payload', 'payload')
483
+ }
484
+
485
+ // Normalize to array
486
+ const transactions = Array.isArray(payload) ? payload : [payload]
487
+
488
+ // Validate we have at least one transaction
489
+ if (transactions.length === 0) {
490
+ return []
491
+ }
492
+
493
+ // Limit batch size to prevent DoS
494
+ const MAX_BATCH_SIZE = 100
495
+ if (transactions.length > MAX_BATCH_SIZE) {
496
+ throw new ValidationError(
497
+ `Batch size exceeds maximum of ${MAX_BATCH_SIZE}`,
498
+ 'payload'
499
+ )
500
+ }
501
+
502
+ const results: WebhookProcessResult[] = []
503
+
504
+ for (const tx of transactions) {
505
+ try {
506
+ // Handle raw vs enhanced webhook format
507
+ if (isRawTransaction(tx)) {
508
+ const result = await processRawTransaction(
509
+ tx,
510
+ viewingPrivateKey,
511
+ spendingPublicKey,
512
+ onPaymentFound
513
+ )
514
+ results.push(result)
515
+ } else {
516
+ // Enhanced transactions don't include log messages,
517
+ // so we can't detect SIP announcements directly
518
+ // For now, skip enhanced transactions
519
+ results.push({
520
+ found: false,
521
+ signature: (tx as HeliusEnhancedTransaction).signature,
522
+ })
523
+ }
524
+ } catch (error) {
525
+ onError?.(error as Error, isRawTransaction(tx) ? tx : undefined)
526
+ results.push({
527
+ found: false,
528
+ signature: getSignature(tx),
529
+ })
530
+ }
531
+ }
532
+
533
+ return results
534
+ }
535
+
536
+ /**
537
+ * Simple handler (backwards compatible)
538
+ */
539
+ const handler = async (payload: HeliusWebhookPayload): Promise<WebhookProcessResult[]> => {
540
+ return processTransactions(payload)
541
+ }
542
+
543
+ /**
544
+ * Process request with full authentication
545
+ * H-4, H-7, H-8 FIX: Signature and auth verification
546
+ */
547
+ handler.processRequest = async (request: WebhookRequest): Promise<WebhookProcessResult[]> => {
548
+ // Verify webhook signature if configured
549
+ if (webhookSecret) {
550
+ if (!verifyWebhookSignature(request.rawBody, request.headers.signature, webhookSecret)) {
551
+ throw new SecurityError(
552
+ 'Invalid webhook signature - request rejected',
553
+ 'INVALID_SIGNATURE'
554
+ )
555
+ }
556
+ }
557
+
558
+ // Verify auth token if configured
559
+ if (authToken) {
560
+ if (!verifyAuthToken(request.headers.authorization, authToken)) {
561
+ throw new SecurityError(
562
+ 'Invalid authorization token - request rejected',
563
+ 'INVALID_AUTH'
564
+ )
565
+ }
566
+ }
567
+
568
+ return processTransactions(request.payload)
569
+ }
570
+
571
+ return handler as WebhookHandler
572
+ }
573
+
574
+ /**
575
+ * Process a single raw transaction for SIP announcements
576
+ */
577
+ async function processRawTransaction(
578
+ tx: HeliusWebhookTransaction,
579
+ viewingPrivateKey: HexString,
580
+ spendingPublicKey: HexString,
581
+ onPaymentFound: (payment: SolanaScanResult) => void | Promise<void>
582
+ ): Promise<WebhookProcessResult> {
583
+ const signature = tx.transaction?.signatures?.[0] ?? 'unknown'
584
+
585
+ // Check if transaction failed
586
+ if (tx.meta?.err) {
587
+ return { found: false, signature }
588
+ }
589
+
590
+ // Ensure log messages exist
591
+ if (!tx.meta?.logMessages) {
592
+ return { found: false, signature }
593
+ }
594
+
595
+ // Search log messages for SIP announcement
596
+ for (const log of tx.meta.logMessages) {
597
+ if (!log.includes(SIP_MEMO_PREFIX)) continue
598
+
599
+ // Extract memo content from log
600
+ const memoMatch = log.match(/Program log: (.+)/)
601
+ if (!memoMatch) continue
602
+
603
+ const memoContent = memoMatch[1]
604
+ const announcement = parseAnnouncement(memoContent)
605
+ if (!announcement) continue
606
+
607
+ // Check if this payment is for us
608
+ // H-5 FIX: Validate ephemeral public key before curve operations
609
+ if (!announcement.ephemeralPublicKey || announcement.ephemeralPublicKey.length < 32) {
610
+ continue // Invalid ephemeral key format, skip
611
+ }
612
+
613
+ let ephemeralPubKeyHex: HexString
614
+ try {
615
+ ephemeralPubKeyHex = solanaAddressToEd25519PublicKey(
616
+ announcement.ephemeralPublicKey
617
+ )
618
+ } catch {
619
+ // Invalid base58 encoding or malformed key
620
+ continue
621
+ }
622
+
623
+ // H-3 FIX: Enhanced view tag validation with strict hex parsing
624
+ // View tags are 1 byte (0-255), represented as 2-char hex string
625
+ const viewTagStr = announcement.viewTag
626
+ if (!viewTagStr || typeof viewTagStr !== 'string') {
627
+ continue
628
+ }
629
+ // Validate hex format (only 0-9, a-f, A-F)
630
+ if (!/^[0-9a-fA-F]{1,2}$/.test(viewTagStr)) {
631
+ continue // Invalid hex format
632
+ }
633
+ const viewTagNumber = parseInt(viewTagStr, 16)
634
+ // Double-check bounds after parse (defense in depth)
635
+ if (!Number.isInteger(viewTagNumber) || viewTagNumber < 0 || viewTagNumber > 255) {
636
+ continue // Invalid view tag, skip this announcement
637
+ }
638
+ const stealthAddressToCheck: StealthAddress = {
639
+ address: announcement.stealthAddress
640
+ ? solanaAddressToEd25519PublicKey(announcement.stealthAddress)
641
+ : ('0x' + '00'.repeat(32)) as HexString,
642
+ ephemeralPublicKey: ephemeralPubKeyHex,
643
+ viewTag: viewTagNumber,
644
+ }
645
+
646
+ // Check if this is our payment (may throw for invalid curve points)
647
+ let isOurs = false
648
+ try {
649
+ isOurs = checkEd25519StealthAddress(
650
+ stealthAddressToCheck,
651
+ viewingPrivateKey,
652
+ spendingPublicKey
653
+ )
654
+ } catch {
655
+ // Invalid ephemeral key or malformed data - not our payment
656
+ continue
657
+ }
658
+
659
+ if (isOurs) {
660
+ // Parse token transfer info using shared utility
661
+ const transferInfo = parseTokenTransferFromBalances(
662
+ tx.meta.preTokenBalances,
663
+ tx.meta.postTokenBalances
664
+ )
665
+
666
+ const payment: SolanaScanResult = {
667
+ stealthAddress: announcement.stealthAddress || '',
668
+ ephemeralPublicKey: announcement.ephemeralPublicKey,
669
+ amount: transferInfo?.amount ?? 0n,
670
+ mint: transferInfo?.mint ?? '',
671
+ tokenSymbol: transferInfo?.mint ? getTokenSymbol(transferInfo.mint) : undefined,
672
+ txSignature: signature,
673
+ slot: tx.slot,
674
+ timestamp: tx.blockTime,
675
+ }
676
+
677
+ // Call the callback (wrap in try-catch to prevent callback errors from breaking processing)
678
+ try {
679
+ await onPaymentFound(payment)
680
+ } catch {
681
+ // Callback error should not prevent returning the found payment
682
+ }
683
+
684
+ return { found: true, payment, signature }
685
+ }
686
+ }
687
+
688
+ return { found: false, signature }
689
+ }
690
+
691
+ // Token transfer parsing and symbol lookup moved to ../utils.ts (L3 fix)
692
+
693
+ /**
694
+ * Type guard for raw Helius webhook transaction
695
+ *
696
+ * Distinguishes between raw and enhanced transaction formats.
697
+ * Raw transactions have transaction.signatures array, enhanced have signature directly.
698
+ *
699
+ * @param tx - Unknown transaction payload
700
+ * @returns True if tx is a raw HeliusWebhookTransaction
701
+ * @internal
702
+ */
703
+ function isRawTransaction(tx: unknown): tx is HeliusWebhookTransaction {
704
+ return (
705
+ typeof tx === 'object' &&
706
+ tx !== null &&
707
+ 'meta' in tx &&
708
+ 'transaction' in tx &&
709
+ Array.isArray((tx as HeliusWebhookTransaction).transaction?.signatures)
710
+ )
711
+ }
712
+
713
+ /**
714
+ * Type guard for enhanced Helius webhook transaction
715
+ *
716
+ * Distinguishes between raw and enhanced transaction formats.
717
+ * Enhanced transactions have signature at top level with type field.
718
+ *
719
+ * @param tx - Unknown transaction payload
720
+ * @returns True if tx is an enhanced HeliusEnhancedTransaction
721
+ * @internal
722
+ */
723
+ function isEnhancedTransaction(tx: unknown): tx is HeliusEnhancedTransaction {
724
+ return (
725
+ typeof tx === 'object' &&
726
+ tx !== null &&
727
+ 'signature' in tx &&
728
+ 'type' in tx &&
729
+ typeof (tx as HeliusEnhancedTransaction).signature === 'string'
730
+ )
731
+ }
732
+
733
+ /**
734
+ * Extract transaction signature from webhook payload
735
+ *
736
+ * Handles both raw and enhanced transaction formats.
737
+ *
738
+ * @param tx - Helius webhook transaction (raw or enhanced)
739
+ * @returns Transaction signature, or 'unknown' if not found
740
+ * @internal
741
+ */
742
+ function getSignature(tx: HeliusWebhookTransaction | HeliusEnhancedTransaction): string {
743
+ // Enhanced transactions have signature at top level
744
+ if ('signature' in tx && typeof (tx as HeliusEnhancedTransaction).signature === 'string') {
745
+ return (tx as HeliusEnhancedTransaction).signature
746
+ }
747
+ // Raw transactions have signatures array in transaction object
748
+ if (isRawTransaction(tx)) {
749
+ return tx.transaction?.signatures?.[0] ?? 'unknown'
750
+ }
751
+ return 'unknown'
752
+ }
753
+
754
+ /**
755
+ * Process a single webhook transaction and check if it's a payment for us
756
+ *
757
+ * Lower-level function for custom webhook handling.
758
+ *
759
+ * @param transaction - Raw Helius webhook transaction
760
+ * @param viewingPrivateKey - Recipient's viewing private key
761
+ * @param spendingPublicKey - Recipient's spending public key
762
+ * @returns Payment result if found, null otherwise
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * const payment = await processWebhookTransaction(
767
+ * webhookPayload,
768
+ * viewingPrivateKey,
769
+ * spendingPublicKey
770
+ * )
771
+ *
772
+ * if (payment) {
773
+ * console.log('Found payment:', payment.amount, payment.mint)
774
+ * }
775
+ * ```
776
+ */
777
+ export async function processWebhookTransaction(
778
+ transaction: HeliusWebhookTransaction,
779
+ viewingPrivateKey: HexString,
780
+ spendingPublicKey: HexString
781
+ ): Promise<SolanaScanResult | null> {
782
+ const result = await processRawTransaction(
783
+ transaction,
784
+ viewingPrivateKey,
785
+ spendingPublicKey,
786
+ () => {} // No-op callback
787
+ )
788
+
789
+ return result.found ? result.payment ?? null : null
790
+ }