@sip-protocol/sdk 0.7.3 → 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.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +47556 -19603
- package/dist/browser.mjs +628 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/{chunk-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/{index-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +252 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +47 -6
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +57 -6
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +23 -0
- package/src/compliance/range-sas.ts +398 -33
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +686 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +110 -29
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -46,8 +46,12 @@ import {
|
|
|
46
46
|
import type { StealthAddress } from '@sip-protocol/types'
|
|
47
47
|
import { parseAnnouncement } from '../types'
|
|
48
48
|
import type { SolanaScanResult } from '../types'
|
|
49
|
-
import { SIP_MEMO_PREFIX
|
|
50
|
-
import {
|
|
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'
|
|
51
55
|
|
|
52
56
|
/**
|
|
53
57
|
* Helius raw webhook payload for a transaction
|
|
@@ -202,10 +206,19 @@ export type HeliusWebhookPayload =
|
|
|
202
206
|
| HeliusEnhancedTransaction[]
|
|
203
207
|
|
|
204
208
|
/**
|
|
205
|
-
* Configuration for webhook handler
|
|
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.
|
|
206
213
|
*/
|
|
207
214
|
export interface WebhookHandlerConfig {
|
|
208
|
-
/**
|
|
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
|
+
*/
|
|
209
222
|
viewingPrivateKey: HexString
|
|
210
223
|
/** Recipient's spending public key (hex) */
|
|
211
224
|
spendingPublicKey: HexString
|
|
@@ -222,6 +235,40 @@ export interface WebhookHandlerConfig {
|
|
|
222
235
|
* @param transaction - The transaction that caused the error (if available)
|
|
223
236
|
*/
|
|
224
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
|
+
}
|
|
225
272
|
}
|
|
226
273
|
|
|
227
274
|
/**
|
|
@@ -236,34 +283,174 @@ export interface WebhookProcessResult {
|
|
|
236
283
|
signature: string
|
|
237
284
|
}
|
|
238
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
|
+
|
|
239
391
|
/**
|
|
240
392
|
* Create a webhook handler for processing Helius webhook payloads
|
|
241
393
|
*
|
|
242
394
|
* @param config - Handler configuration
|
|
243
|
-
* @returns
|
|
395
|
+
* @returns Handler function to process webhook payloads
|
|
244
396
|
*
|
|
245
|
-
* @example
|
|
397
|
+
* @example Simple usage (not recommended for production)
|
|
246
398
|
* ```typescript
|
|
247
399
|
* const handler = createWebhookHandler({
|
|
248
400
|
* viewingPrivateKey: recipientKeys.viewingPrivateKey,
|
|
249
401
|
* spendingPublicKey: recipientKeys.spendingPublicKey,
|
|
250
402
|
* onPaymentFound: async (payment) => {
|
|
251
403
|
* await db.savePayment(payment)
|
|
252
|
-
* await notifyUser(payment)
|
|
253
404
|
* },
|
|
254
405
|
* })
|
|
255
406
|
*
|
|
256
|
-
* // In your Express route:
|
|
257
407
|
* app.post('/webhook', async (req, res) => {
|
|
258
408
|
* const results = await handler(req.body)
|
|
259
409
|
* res.json({ processed: results.length })
|
|
260
410
|
* })
|
|
261
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
|
+
* ```
|
|
262
444
|
*/
|
|
263
|
-
export function createWebhookHandler(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
445
|
+
export function createWebhookHandler(config: WebhookHandlerConfig): WebhookHandler {
|
|
446
|
+
const {
|
|
447
|
+
viewingPrivateKey,
|
|
448
|
+
spendingPublicKey,
|
|
449
|
+
onPaymentFound,
|
|
450
|
+
onError,
|
|
451
|
+
webhookSecret,
|
|
452
|
+
authToken,
|
|
453
|
+
} = config
|
|
267
454
|
|
|
268
455
|
// Validate required keys
|
|
269
456
|
if (!viewingPrivateKey || !viewingPrivateKey.startsWith('0x')) {
|
|
@@ -276,9 +463,42 @@ export function createWebhookHandler(
|
|
|
276
463
|
throw new ValidationError('onPaymentFound callback is required', 'onPaymentFound')
|
|
277
464
|
}
|
|
278
465
|
|
|
279
|
-
|
|
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
|
+
|
|
280
485
|
// Normalize to array
|
|
281
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
|
+
|
|
282
502
|
const results: WebhookProcessResult[] = []
|
|
283
503
|
|
|
284
504
|
for (const tx of transactions) {
|
|
@@ -312,6 +532,43 @@ export function createWebhookHandler(
|
|
|
312
532
|
|
|
313
533
|
return results
|
|
314
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
|
|
315
572
|
}
|
|
316
573
|
|
|
317
574
|
/**
|
|
@@ -348,13 +605,34 @@ async function processRawTransaction(
|
|
|
348
605
|
if (!announcement) continue
|
|
349
606
|
|
|
350
607
|
// Check if this payment is for us
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
+
}
|
|
354
612
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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) {
|
|
358
636
|
continue // Invalid view tag, skip this announcement
|
|
359
637
|
}
|
|
360
638
|
const stealthAddressToCheck: StealthAddress = {
|
|
@@ -379,8 +657,11 @@ async function processRawTransaction(
|
|
|
379
657
|
}
|
|
380
658
|
|
|
381
659
|
if (isOurs) {
|
|
382
|
-
// Parse token transfer info
|
|
383
|
-
const transferInfo =
|
|
660
|
+
// Parse token transfer info using shared utility
|
|
661
|
+
const transferInfo = parseTokenTransferFromBalances(
|
|
662
|
+
tx.meta.preTokenBalances,
|
|
663
|
+
tx.meta.postTokenBalances
|
|
664
|
+
)
|
|
384
665
|
|
|
385
666
|
const payment: SolanaScanResult = {
|
|
386
667
|
stealthAddress: announcement.stealthAddress || '',
|
|
@@ -407,53 +688,17 @@ async function processRawTransaction(
|
|
|
407
688
|
return { found: false, signature }
|
|
408
689
|
}
|
|
409
690
|
|
|
410
|
-
|
|
411
|
-
* Parse token transfer info from webhook transaction
|
|
412
|
-
*/
|
|
413
|
-
function parseTokenTransferFromWebhook(
|
|
414
|
-
tx: HeliusWebhookTransaction
|
|
415
|
-
): { mint: string; amount: bigint } | null {
|
|
416
|
-
const { preTokenBalances, postTokenBalances } = tx.meta
|
|
417
|
-
|
|
418
|
-
if (!postTokenBalances || !preTokenBalances) {
|
|
419
|
-
return null
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Find token balance changes
|
|
423
|
-
for (const post of postTokenBalances) {
|
|
424
|
-
const pre = preTokenBalances.find(
|
|
425
|
-
(p) => p.accountIndex === post.accountIndex
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
const postAmount = BigInt(post.uiTokenAmount.amount)
|
|
429
|
-
const preAmount = pre ? BigInt(pre.uiTokenAmount.amount) : 0n
|
|
430
|
-
|
|
431
|
-
if (postAmount > preAmount) {
|
|
432
|
-
return {
|
|
433
|
-
mint: post.mint,
|
|
434
|
-
amount: postAmount - preAmount,
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return null
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Get token symbol from mint address
|
|
444
|
-
*/
|
|
445
|
-
function getTokenSymbol(mint: string): string | undefined {
|
|
446
|
-
for (const [symbol, address] of Object.entries(SOLANA_TOKEN_MINTS)) {
|
|
447
|
-
if (address === mint) {
|
|
448
|
-
return symbol
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return undefined
|
|
452
|
-
}
|
|
691
|
+
// Token transfer parsing and symbol lookup moved to ../utils.ts (L3 fix)
|
|
453
692
|
|
|
454
693
|
/**
|
|
455
|
-
* Type guard for raw transaction
|
|
456
|
-
*
|
|
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
|
|
457
702
|
*/
|
|
458
703
|
function isRawTransaction(tx: unknown): tx is HeliusWebhookTransaction {
|
|
459
704
|
return (
|
|
@@ -466,7 +711,33 @@ function isRawTransaction(tx: unknown): tx is HeliusWebhookTransaction {
|
|
|
466
711
|
}
|
|
467
712
|
|
|
468
713
|
/**
|
|
469
|
-
*
|
|
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
|
|
470
741
|
*/
|
|
471
742
|
function getSignature(tx: HeliusWebhookTransaction | HeliusEnhancedTransaction): string {
|
|
472
743
|
// Enhanced transactions have signature at top level
|