@sip-protocol/sdk 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +267 -0
  2. package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
  3. package/dist/browser.d.mts +10 -4
  4. package/dist/browser.d.ts +10 -4
  5. package/dist/browser.js +47556 -19603
  6. package/dist/browser.mjs +628 -48
  7. package/dist/chunk-4GRJ5MAW.mjs +152 -0
  8. package/dist/chunk-5D7A3L3W.mjs +717 -0
  9. package/dist/chunk-64AYA5F5.mjs +7834 -0
  10. package/dist/chunk-GMDGB22A.mjs +379 -0
  11. package/dist/chunk-I534WKN7.mjs +328 -0
  12. package/dist/chunk-IBZVA5Y7.mjs +1003 -0
  13. package/dist/chunk-PRRZAWJE.mjs +223 -0
  14. package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
  15. package/dist/{chunk-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
  16. package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
  17. package/dist/constants-LHAAUC2T.mjs +51 -0
  18. package/dist/dist-2OGQ7FED.mjs +3957 -0
  19. package/dist/dist-IFHPYLDX.mjs +254 -0
  20. package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
  21. package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
  22. package/dist/{index-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
  23. package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
  24. package/dist/index.d.mts +9 -3
  25. package/dist/index.d.ts +9 -3
  26. package/dist/index.js +48396 -19623
  27. package/dist/index.mjs +537 -19
  28. package/dist/interface-Bf7w1PLW.d.mts +679 -0
  29. package/dist/interface-Bf7w1PLW.d.ts +679 -0
  30. package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
  31. package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
  32. package/dist/proofs/halo2.d.mts +151 -0
  33. package/dist/proofs/halo2.d.ts +151 -0
  34. package/dist/proofs/halo2.js +350 -0
  35. package/dist/proofs/halo2.mjs +11 -0
  36. package/dist/proofs/kimchi.d.mts +160 -0
  37. package/dist/proofs/kimchi.d.ts +160 -0
  38. package/dist/proofs/kimchi.js +431 -0
  39. package/dist/proofs/kimchi.mjs +13 -0
  40. package/dist/proofs/noir.d.mts +1 -1
  41. package/dist/proofs/noir.d.ts +1 -1
  42. package/dist/proofs/noir.js +74 -18
  43. package/dist/proofs/noir.mjs +84 -24
  44. package/dist/solana-U3MEGU7W.mjs +280 -0
  45. package/dist/validity_proof-3POXLPNY.mjs +21 -0
  46. package/package.json +44 -11
  47. package/src/adapters/index.ts +41 -0
  48. package/src/adapters/jupiter.ts +571 -0
  49. package/src/adapters/near-intents.ts +135 -0
  50. package/src/advisor/advisor.ts +653 -0
  51. package/src/advisor/index.ts +54 -0
  52. package/src/advisor/tools.ts +303 -0
  53. package/src/advisor/types.ts +164 -0
  54. package/src/chains/ethereum/announcement.ts +536 -0
  55. package/src/chains/ethereum/bnb-optimizations.ts +474 -0
  56. package/src/chains/ethereum/commitment.ts +522 -0
  57. package/src/chains/ethereum/constants.ts +462 -0
  58. package/src/chains/ethereum/deployment.ts +596 -0
  59. package/src/chains/ethereum/gas-estimation.ts +538 -0
  60. package/src/chains/ethereum/index.ts +268 -0
  61. package/src/chains/ethereum/optimizations.ts +614 -0
  62. package/src/chains/ethereum/privacy-adapter.ts +855 -0
  63. package/src/chains/ethereum/registry.ts +584 -0
  64. package/src/chains/ethereum/rpc.ts +905 -0
  65. package/src/chains/ethereum/stealth.ts +491 -0
  66. package/src/chains/ethereum/token.ts +790 -0
  67. package/src/chains/ethereum/transfer.ts +637 -0
  68. package/src/chains/ethereum/types.ts +456 -0
  69. package/src/chains/ethereum/viewing-key.ts +455 -0
  70. package/src/chains/near/commitment.ts +608 -0
  71. package/src/chains/near/constants.ts +284 -0
  72. package/src/chains/near/function-call.ts +871 -0
  73. package/src/chains/near/history.ts +654 -0
  74. package/src/chains/near/implicit-account.ts +840 -0
  75. package/src/chains/near/index.ts +393 -0
  76. package/src/chains/near/native-transfer.ts +658 -0
  77. package/src/chains/near/nep141.ts +775 -0
  78. package/src/chains/near/privacy-adapter.ts +889 -0
  79. package/src/chains/near/resolver.ts +971 -0
  80. package/src/chains/near/rpc.ts +1016 -0
  81. package/src/chains/near/stealth.ts +419 -0
  82. package/src/chains/near/types.ts +317 -0
  83. package/src/chains/near/viewing-key.ts +876 -0
  84. package/src/chains/solana/anchor-transfer.ts +386 -0
  85. package/src/chains/solana/commitment.ts +577 -0
  86. package/src/chains/solana/constants.ts +126 -12
  87. package/src/chains/solana/ephemeral-keys.ts +543 -0
  88. package/src/chains/solana/index.ts +252 -1
  89. package/src/chains/solana/key-derivation.ts +418 -0
  90. package/src/chains/solana/kit-compat.ts +334 -0
  91. package/src/chains/solana/optimizations.ts +560 -0
  92. package/src/chains/solana/privacy-adapter.ts +605 -0
  93. package/src/chains/solana/providers/generic.ts +47 -6
  94. package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
  95. package/src/chains/solana/providers/helius-enhanced.ts +623 -0
  96. package/src/chains/solana/providers/helius.ts +186 -33
  97. package/src/chains/solana/providers/index.ts +31 -0
  98. package/src/chains/solana/providers/interface.ts +61 -18
  99. package/src/chains/solana/providers/quicknode.ts +409 -0
  100. package/src/chains/solana/providers/triton.ts +426 -0
  101. package/src/chains/solana/providers/webhook.ts +338 -67
  102. package/src/chains/solana/rpc-client.ts +1150 -0
  103. package/src/chains/solana/scan.ts +83 -66
  104. package/src/chains/solana/sol-transfer.ts +732 -0
  105. package/src/chains/solana/spl-transfer.ts +886 -0
  106. package/src/chains/solana/stealth-scanner.ts +703 -0
  107. package/src/chains/solana/sunspot-verifier.ts +453 -0
  108. package/src/chains/solana/transaction-builder.ts +755 -0
  109. package/src/chains/solana/transfer.ts +74 -5
  110. package/src/chains/solana/types.ts +57 -6
  111. package/src/chains/solana/utils.ts +110 -0
  112. package/src/chains/solana/viewing-key.ts +807 -0
  113. package/src/compliance/fireblocks.ts +921 -0
  114. package/src/compliance/index.ts +23 -0
  115. package/src/compliance/range-sas.ts +398 -33
  116. package/src/config/endpoints.ts +100 -0
  117. package/src/crypto.ts +11 -8
  118. package/src/errors.ts +82 -0
  119. package/src/evm/erc4337-relayer.ts +830 -0
  120. package/src/evm/index.ts +47 -0
  121. package/src/fees/calculator.ts +396 -0
  122. package/src/fees/index.ts +87 -0
  123. package/src/fees/near-contract.ts +429 -0
  124. package/src/fees/types.ts +268 -0
  125. package/src/index.ts +686 -1
  126. package/src/intent.ts +6 -3
  127. package/src/logger.ts +324 -0
  128. package/src/network/index.ts +80 -0
  129. package/src/network/proxy.ts +691 -0
  130. package/src/optimizations/index.ts +541 -0
  131. package/src/oracle/types.ts +1 -0
  132. package/src/privacy-backends/arcium-types.ts +727 -0
  133. package/src/privacy-backends/arcium.ts +719 -0
  134. package/src/privacy-backends/combined-privacy.ts +866 -0
  135. package/src/privacy-backends/cspl-token.ts +595 -0
  136. package/src/privacy-backends/cspl-types.ts +512 -0
  137. package/src/privacy-backends/cspl.ts +907 -0
  138. package/src/privacy-backends/health.ts +488 -0
  139. package/src/privacy-backends/inco-types.ts +323 -0
  140. package/src/privacy-backends/inco.ts +616 -0
  141. package/src/privacy-backends/index.ts +254 -4
  142. package/src/privacy-backends/interface.ts +649 -6
  143. package/src/privacy-backends/lru-cache.ts +343 -0
  144. package/src/privacy-backends/magicblock.ts +458 -0
  145. package/src/privacy-backends/mock.ts +258 -0
  146. package/src/privacy-backends/privacycash.ts +13 -17
  147. package/src/privacy-backends/private-swap.ts +570 -0
  148. package/src/privacy-backends/rate-limiter.ts +683 -0
  149. package/src/privacy-backends/registry.ts +414 -2
  150. package/src/privacy-backends/router.ts +283 -3
  151. package/src/privacy-backends/shadowwire.ts +449 -0
  152. package/src/privacy-backends/sip-native.ts +3 -0
  153. package/src/privacy-logger.ts +191 -0
  154. package/src/production-safety.ts +373 -0
  155. package/src/proofs/aggregator.ts +1029 -0
  156. package/src/proofs/browser-composer.ts +1150 -0
  157. package/src/proofs/browser.ts +113 -25
  158. package/src/proofs/cache/index.ts +127 -0
  159. package/src/proofs/cache/interface.ts +545 -0
  160. package/src/proofs/cache/key-generator.ts +188 -0
  161. package/src/proofs/cache/lru-cache.ts +481 -0
  162. package/src/proofs/cache/multi-tier-cache.ts +575 -0
  163. package/src/proofs/cache/persistent-cache.ts +788 -0
  164. package/src/proofs/compliance-proof.ts +872 -0
  165. package/src/proofs/composer/base.ts +923 -0
  166. package/src/proofs/composer/index.ts +25 -0
  167. package/src/proofs/composer/interface.ts +518 -0
  168. package/src/proofs/composer/types.ts +383 -0
  169. package/src/proofs/converters/halo2.ts +452 -0
  170. package/src/proofs/converters/index.ts +208 -0
  171. package/src/proofs/converters/interface.ts +363 -0
  172. package/src/proofs/converters/kimchi.ts +462 -0
  173. package/src/proofs/converters/noir.ts +451 -0
  174. package/src/proofs/fallback.ts +888 -0
  175. package/src/proofs/halo2.ts +42 -0
  176. package/src/proofs/index.ts +471 -0
  177. package/src/proofs/interface.ts +13 -0
  178. package/src/proofs/kimchi.ts +42 -0
  179. package/src/proofs/lazy.ts +1004 -0
  180. package/src/proofs/mock.ts +25 -1
  181. package/src/proofs/noir.ts +110 -29
  182. package/src/proofs/orchestrator.ts +960 -0
  183. package/src/proofs/parallel/concurrency.ts +297 -0
  184. package/src/proofs/parallel/dependency-graph.ts +602 -0
  185. package/src/proofs/parallel/executor.ts +420 -0
  186. package/src/proofs/parallel/index.ts +131 -0
  187. package/src/proofs/parallel/interface.ts +685 -0
  188. package/src/proofs/parallel/worker-pool.ts +644 -0
  189. package/src/proofs/providers/halo2.ts +560 -0
  190. package/src/proofs/providers/index.ts +34 -0
  191. package/src/proofs/providers/kimchi.ts +641 -0
  192. package/src/proofs/validator.ts +881 -0
  193. package/src/proofs/verifier.ts +867 -0
  194. package/src/quantum/index.ts +112 -0
  195. package/src/quantum/winternitz-vault.ts +639 -0
  196. package/src/quantum/wots.ts +611 -0
  197. package/src/settlement/backends/direct-chain.ts +1 -0
  198. package/src/settlement/index.ts +9 -0
  199. package/src/settlement/router.ts +732 -46
  200. package/src/solana/index.ts +72 -0
  201. package/src/solana/jito-relayer.ts +687 -0
  202. package/src/solana/noir-verifier-types.ts +430 -0
  203. package/src/solana/noir-verifier.ts +816 -0
  204. package/src/stealth/address-derivation.ts +193 -0
  205. package/src/stealth/ed25519.ts +431 -0
  206. package/src/stealth/index.ts +233 -0
  207. package/src/stealth/meta-address.ts +221 -0
  208. package/src/stealth/secp256k1.ts +368 -0
  209. package/src/stealth/utils.ts +194 -0
  210. package/src/stealth.ts +50 -1504
  211. package/src/sync/index.ts +106 -0
  212. package/src/sync/manager.ts +504 -0
  213. package/src/sync/mock-provider.ts +318 -0
  214. package/src/sync/oblivious.ts +625 -0
  215. package/src/tokens/index.ts +15 -0
  216. package/src/tokens/registry.ts +301 -0
  217. package/src/utils/deprecation.ts +94 -0
  218. package/src/utils/index.ts +9 -0
  219. package/src/wallet/ethereum/index.ts +68 -0
  220. package/src/wallet/ethereum/metamask-privacy.ts +420 -0
  221. package/src/wallet/ethereum/multi-wallet.ts +646 -0
  222. package/src/wallet/ethereum/privacy-adapter.ts +700 -0
  223. package/src/wallet/ethereum/types.ts +3 -1
  224. package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
  225. package/src/wallet/hardware/index.ts +10 -0
  226. package/src/wallet/hardware/ledger-privacy.ts +414 -0
  227. package/src/wallet/index.ts +71 -0
  228. package/src/wallet/near/adapter.ts +626 -0
  229. package/src/wallet/near/index.ts +86 -0
  230. package/src/wallet/near/meteor-wallet.ts +1153 -0
  231. package/src/wallet/near/my-near-wallet.ts +790 -0
  232. package/src/wallet/near/wallet-selector.ts +702 -0
  233. package/src/wallet/solana/adapter.ts +6 -4
  234. package/src/wallet/solana/index.ts +13 -0
  235. package/src/wallet/solana/privacy-adapter.ts +567 -0
  236. package/src/wallet/sui/types.ts +6 -4
  237. package/src/zcash/rpc-client.ts +13 -6
  238. package/dist/chunk-2XIVXWHA.mjs +0 -1930
  239. package/dist/chunk-3INS3PR5.mjs +0 -884
  240. package/dist/chunk-3OVABDRH.mjs +0 -17096
  241. package/dist/chunk-7RFRWDCW.mjs +0 -1504
  242. package/dist/chunk-DLDWZFYC.mjs +0 -1495
  243. package/dist/chunk-E6SZWREQ.mjs +0 -57
  244. package/dist/chunk-F6F73W35.mjs +0 -16166
  245. package/dist/chunk-G33LB27A.mjs +0 -16166
  246. package/dist/chunk-HGU6HZRC.mjs +0 -231
  247. package/dist/chunk-L2K34JCU.mjs +0 -1496
  248. package/dist/chunk-OFDBEIEK.mjs +0 -16166
  249. package/dist/chunk-SF7YSLF5.mjs +0 -1515
  250. package/dist/chunk-SN4ZDTVW.mjs +0 -16166
  251. package/dist/chunk-WWUSGOXE.mjs +0 -17129
  252. package/dist/constants-VOI7BSLK.mjs +0 -27
  253. package/dist/index-B71aXVzk.d.ts +0 -13264
  254. package/dist/index-BYZbDjal.d.ts +0 -11390
  255. package/dist/index-CHB3KuOB.d.mts +0 -11859
  256. package/dist/index-CzWPI6Le.d.ts +0 -11859
  257. package/dist/index-pOIIuwfV.d.mts +0 -13264
  258. package/dist/index-xbWjohNq.d.mts +0 -11390
  259. package/dist/solana-4O4K45VU.mjs +0 -46
  260. package/dist/solana-5EMCTPTS.mjs +0 -46
  261. package/dist/solana-NDABAZ6P.mjs +0 -56
  262. package/dist/solana-Q4NAVBTS.mjs +0 -46
  263. package/dist/solana-ZYO63LY5.mjs +0 -46
@@ -21,6 +21,73 @@
21
21
  */
22
22
 
23
23
  import type { SolanaRPCProvider, TokenAsset, ProviderConfig } from './interface'
24
+ import { ValidationError, NetworkError } from '../../../errors'
25
+ import {
26
+ SOLANA_ADDRESS_MIN_LENGTH,
27
+ SOLANA_ADDRESS_MAX_LENGTH,
28
+ HELIUS_API_KEY_MIN_LENGTH,
29
+ HELIUS_DAS_PAGE_LIMIT,
30
+ HELIUS_MAX_PAGES,
31
+ sanitizeUrl,
32
+ } from '../constants'
33
+
34
+ /** Default fetch timeout in milliseconds */
35
+ const DEFAULT_FETCH_TIMEOUT_MS = 30000
36
+
37
+ /**
38
+ * Mask API key for safe logging/error messages
39
+ *
40
+ * Shows only first 4 and last 4 characters to prevent key exposure.
41
+ *
42
+ * @param apiKey - Helius API key to mask
43
+ * @returns Masked key (e.g., 'abcd...wxyz') or '***' if too short
44
+ * @internal
45
+ */
46
+ function maskApiKey(apiKey: string): string {
47
+ if (apiKey.length <= HELIUS_API_KEY_MIN_LENGTH) return '***'
48
+ return `${apiKey.slice(0, 4)}...${apiKey.slice(-4)}`
49
+ }
50
+
51
+ /**
52
+ * Fetch with configurable timeout using AbortController
53
+ *
54
+ * Wraps fetch with a timeout to prevent hanging requests.
55
+ *
56
+ * @param url - URL to fetch
57
+ * @param options - Fetch options (method, headers, body, etc.)
58
+ * @param timeoutMs - Timeout in milliseconds (default: 30000)
59
+ * @returns Fetch response
60
+ * @throws NetworkError if request times out
61
+ * @internal
62
+ */
63
+ async function fetchWithTimeout(
64
+ url: string,
65
+ options: RequestInit,
66
+ timeoutMs: number = DEFAULT_FETCH_TIMEOUT_MS
67
+ ): Promise<Response> {
68
+ const controller = new AbortController()
69
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
70
+
71
+ try {
72
+ const response = await fetch(url, {
73
+ ...options,
74
+ signal: controller.signal,
75
+ })
76
+ return response
77
+ } catch (error) {
78
+ if (error instanceof Error && error.name === 'AbortError') {
79
+ // H-2 FIX: Sanitize URL to prevent credential exposure
80
+ throw new NetworkError(
81
+ `Request timeout after ${timeoutMs}ms`,
82
+ undefined,
83
+ { endpoint: sanitizeUrl(url) }
84
+ )
85
+ }
86
+ throw error
87
+ } finally {
88
+ clearTimeout(timeoutId)
89
+ }
90
+ }
24
91
 
25
92
  /**
26
93
  * Helius API response types
@@ -79,9 +146,17 @@ interface HeliusBalancesResponse {
79
146
 
80
147
  /**
81
148
  * Helius provider configuration
149
+ *
150
+ * @security API keys should be treated as sensitive credentials.
82
151
  */
83
152
  export interface HeliusProviderConfig extends ProviderConfig {
84
- /** Helius API key (required) */
153
+ /**
154
+ * Helius API key (required)
155
+ *
156
+ * @security Treat as sensitive credential. Use environment variables.
157
+ * Never commit to source control or log in error messages.
158
+ * The SDK masks this key in error messages automatically.
159
+ */
85
160
  apiKey: string
86
161
  /** Solana cluster (default: mainnet-beta) */
87
162
  cluster?: 'mainnet-beta' | 'devnet'
@@ -95,28 +170,41 @@ export interface HeliusProviderConfig extends ProviderConfig {
95
170
  */
96
171
  export class HeliusProvider implements SolanaRPCProvider {
97
172
  readonly name = 'helius'
98
- private apiKey: string
99
- private cluster: 'mainnet-beta' | 'devnet'
100
- private rpcUrl: string
101
- private restUrl: string
173
+ private readonly apiKey: string
174
+ private readonly cluster: 'mainnet-beta' | 'devnet'
175
+ private readonly rpcUrl: string
176
+ private readonly restUrl: string
102
177
 
103
178
  constructor(config: HeliusProviderConfig) {
179
+ // Validate API key
104
180
  if (!config.apiKey) {
105
- throw new Error('Helius API key is required. Get one at https://dev.helius.xyz')
181
+ throw new ValidationError(
182
+ 'Helius API key is required. Get one at https://dev.helius.xyz',
183
+ 'apiKey'
184
+ )
185
+ }
186
+
187
+ // Validate API key format (basic check - Helius keys are UUIDs or alphanumeric)
188
+ if (typeof config.apiKey !== 'string' || config.apiKey.length < HELIUS_API_KEY_MIN_LENGTH) {
189
+ throw new ValidationError(
190
+ 'Invalid Helius API key format',
191
+ 'apiKey'
192
+ )
106
193
  }
107
194
 
108
195
  this.apiKey = config.apiKey
109
196
  this.cluster = config.cluster ?? 'mainnet-beta'
110
197
 
111
- // RPC endpoint for DAS API
198
+ // RPC endpoint for DAS API (no API key in URL - use header instead)
199
+ // H-1 FIX: API key moved from URL query parameter to Authorization header
112
200
  this.rpcUrl = this.cluster === 'devnet'
113
- ? `https://devnet.helius-rpc.com/?api-key=${this.apiKey}`
114
- : `https://mainnet.helius-rpc.com/?api-key=${this.apiKey}`
201
+ ? 'https://devnet.helius-rpc.com'
202
+ : 'https://mainnet.helius-rpc.com'
115
203
 
116
204
  // REST endpoint for balances API
117
205
  this.restUrl = this.cluster === 'devnet'
118
- ? `https://api-devnet.helius.xyz/v0`
119
- : `https://api.helius.xyz/v0`
206
+ ? 'https://api-devnet.helius.xyz/v0'
207
+ : 'https://api.helius.xyz/v0'
120
208
  }
121
209
 
122
210
  /**
@@ -126,15 +214,28 @@ export class HeliusProvider implements SolanaRPCProvider {
126
214
  * NFTs and fungible tokens with metadata.
127
215
  */
128
216
  async getAssetsByOwner(owner: string): Promise<TokenAsset[]> {
217
+ // Validate owner address
218
+ if (!owner || typeof owner !== 'string') {
219
+ throw new ValidationError('owner address is required', 'owner')
220
+ }
221
+ // Basic Solana address validation (32-44 chars, base58)
222
+ if (owner.length < SOLANA_ADDRESS_MIN_LENGTH || owner.length > SOLANA_ADDRESS_MAX_LENGTH) {
223
+ throw new ValidationError('invalid Solana address format', 'owner')
224
+ }
225
+
129
226
  const assets: TokenAsset[] = []
130
227
  let page = 1
131
- const limit = 1000
228
+ const limit = HELIUS_DAS_PAGE_LIMIT
132
229
  let hasMore = true
133
230
 
134
231
  while (hasMore) {
135
- const response = await fetch(this.rpcUrl, {
232
+ const response = await fetchWithTimeout(this.rpcUrl, {
136
233
  method: 'POST',
137
- headers: { 'Content-Type': 'application/json' },
234
+ headers: {
235
+ 'Content-Type': 'application/json',
236
+ // H-1 FIX: Use Authorization header instead of URL query parameter
237
+ 'Authorization': `Bearer ${this.apiKey}`,
238
+ },
138
239
  body: JSON.stringify({
139
240
  jsonrpc: '2.0',
140
241
  id: `sip-${Date.now()}`,
@@ -152,14 +253,23 @@ export class HeliusProvider implements SolanaRPCProvider {
152
253
  })
153
254
 
154
255
  if (!response.ok) {
155
- throw new Error(`Helius API error: ${response.status} ${response.statusText}`)
256
+ // H-2 FIX: Never include API key in error messages, sanitize URLs
257
+ throw new NetworkError(
258
+ `Helius API error: ${response.status} ${response.statusText} (key: ${maskApiKey(this.apiKey)})`,
259
+ undefined,
260
+ { endpoint: sanitizeUrl(this.rpcUrl), statusCode: response.status }
261
+ )
156
262
  }
157
263
 
158
264
  const data = (await response.json()) as HeliusDASResponse
159
265
 
160
266
  // Handle JSON-RPC errors
161
267
  if (data.error) {
162
- throw new Error(`Helius RPC error: ${data.error.message} (code: ${data.error.code})`)
268
+ throw new NetworkError(
269
+ `Helius RPC error: ${data.error.message} (code: ${data.error.code})`,
270
+ undefined,
271
+ { endpoint: sanitizeUrl(this.rpcUrl) }
272
+ )
163
273
  }
164
274
 
165
275
  if (data.result?.items) {
@@ -174,10 +284,14 @@ export class HeliusProvider implements SolanaRPCProvider {
174
284
  if (!tokenInfo?.balance) continue
175
285
 
176
286
  // Convert balance to BigInt, handling both string and number types
177
- // String is preferred for large values to preserve precision
178
- const balanceValue = typeof tokenInfo.balance === 'string'
179
- ? BigInt(tokenInfo.balance)
180
- : BigInt(Math.floor(tokenInfo.balance))
287
+ // Always use string parsing for BigInt to avoid precision loss
288
+ let balanceValue: bigint
289
+ if (typeof tokenInfo.balance === 'string') {
290
+ balanceValue = BigInt(tokenInfo.balance)
291
+ } else {
292
+ // For numbers, convert to string first to avoid precision loss
293
+ balanceValue = BigInt(Math.floor(tokenInfo.balance).toString())
294
+ }
181
295
 
182
296
  assets.push({
183
297
  mint: item.id,
@@ -195,8 +309,7 @@ export class HeliusProvider implements SolanaRPCProvider {
195
309
  page++
196
310
 
197
311
  // Safety limit to prevent infinite loops
198
- if (page > 100) {
199
- console.warn('[HeliusProvider] Reached page limit (100), stopping pagination')
312
+ if (page > HELIUS_MAX_PAGES) {
200
313
  break
201
314
  }
202
315
  }
@@ -210,16 +323,43 @@ export class HeliusProvider implements SolanaRPCProvider {
210
323
  * More efficient than getAssetsByOwner when you only need one token's balance.
211
324
  */
212
325
  async getTokenBalance(owner: string, mint: string): Promise<bigint> {
213
- try {
214
- const url = `${this.restUrl}/addresses/${owner}/balances?api-key=${this.apiKey}`
326
+ // Validate inputs
327
+ if (!owner || typeof owner !== 'string') {
328
+ throw new ValidationError('owner address is required', 'owner')
329
+ }
330
+ if (!mint || typeof mint !== 'string') {
331
+ throw new ValidationError('mint address is required', 'mint')
332
+ }
333
+ // Validate address format
334
+ if (owner.length < SOLANA_ADDRESS_MIN_LENGTH || owner.length > SOLANA_ADDRESS_MAX_LENGTH) {
335
+ throw new ValidationError('invalid owner address format', 'owner')
336
+ }
337
+ if (mint.length < SOLANA_ADDRESS_MIN_LENGTH || mint.length > SOLANA_ADDRESS_MAX_LENGTH) {
338
+ throw new ValidationError('invalid mint address format', 'mint')
339
+ }
215
340
 
216
- const response = await fetch(url)
341
+ const url = `${this.restUrl}/addresses/${owner}/balances`
342
+
343
+ try {
344
+ const response = await fetchWithTimeout(url, {
345
+ headers: {
346
+ 'Authorization': `Bearer ${this.apiKey}`,
347
+ },
348
+ })
217
349
 
218
350
  if (!response.ok) {
219
- // Fallback to DAS if balances API fails
220
- const assets = await this.getAssetsByOwner(owner)
221
- const asset = assets.find((a) => a.mint === mint)
222
- return asset?.amount ?? 0n
351
+ // Only fallback for specific recoverable errors (404, 503)
352
+ // Don't fallback for auth errors (401, 403) or client errors (400)
353
+ if (response.status === 404 || response.status === 503) {
354
+ return this.getTokenBalanceFallback(owner, mint)
355
+ }
356
+ // For other errors, throw rather than silently fallback
357
+ // H-2 FIX: Sanitize URL to prevent credential exposure
358
+ throw new NetworkError(
359
+ `Helius Balances API error: ${response.status}`,
360
+ undefined,
361
+ { endpoint: sanitizeUrl(url), statusCode: response.status }
362
+ )
223
363
  }
224
364
 
225
365
  const data = (await response.json()) as HeliusBalancesResponse
@@ -227,13 +367,26 @@ export class HeliusProvider implements SolanaRPCProvider {
227
367
  const token = data.tokens?.find((t) => t.mint === mint)
228
368
  return token ? BigInt(token.amount) : 0n
229
369
  } catch (error) {
230
- console.warn('[HeliusProvider] getTokenBalance error, falling back to DAS:', error)
231
- const assets = await this.getAssetsByOwner(owner)
232
- const asset = assets.find((a) => a.mint === mint)
233
- return asset?.amount ?? 0n
370
+ // Only fallback for network/timeout errors, not all errors
371
+ if (error instanceof NetworkError && error.message.includes('timeout')) {
372
+ return this.getTokenBalanceFallback(owner, mint)
373
+ }
374
+ // Re-throw validation and other errors
375
+ throw error
234
376
  }
235
377
  }
236
378
 
379
+ /**
380
+ * Fallback method to DAS API for token balance
381
+ * Only called for specific recoverable errors
382
+ * @internal
383
+ */
384
+ private async getTokenBalanceFallback(owner: string, mint: string): Promise<bigint> {
385
+ const assets = await this.getAssetsByOwner(owner)
386
+ const asset = assets.find((a) => a.mint === mint)
387
+ return asset?.amount ?? 0n
388
+ }
389
+
237
390
  /**
238
391
  * Check if provider supports real-time subscriptions
239
392
  *
@@ -41,14 +41,45 @@ export {
41
41
  // Provider implementations
42
42
  export { HeliusProvider, type HeliusProviderConfig } from './helius'
43
43
  export { GenericProvider } from './generic'
44
+ export { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
45
+ export { TritonProvider, type TritonProviderConfig } from './triton'
44
46
 
45
47
  // Webhook handler for real-time scanning
46
48
  export {
47
49
  createWebhookHandler,
48
50
  processWebhookTransaction,
51
+ verifyWebhookSignature,
52
+ verifyAuthToken,
49
53
  type HeliusWebhookTransaction,
50
54
  type HeliusEnhancedTransaction,
51
55
  type HeliusWebhookPayload,
52
56
  type WebhookHandlerConfig,
53
57
  type WebhookProcessResult,
58
+ type WebhookRequest,
59
+ type WebhookHandler,
54
60
  } from './webhook'
61
+
62
+ // Enhanced Transactions API for human-readable tx data
63
+ export {
64
+ HeliusEnhanced,
65
+ createHeliusEnhanced,
66
+ type HeliusEnhancedConfig,
67
+ } from './helius-enhanced'
68
+
69
+ // Enhanced Transactions types
70
+ export type {
71
+ EnhancedTransactionType,
72
+ NativeTransfer,
73
+ TokenTransfer,
74
+ NftTransfer,
75
+ SwapEvent,
76
+ EnhancedTransactionEvents,
77
+ EnhancedAccountData,
78
+ EnhancedTransaction,
79
+ ParseTransactionsOptions,
80
+ GetTransactionHistoryOptions,
81
+ PrivacyDisplayOptions,
82
+ SIPTransactionMetadata,
83
+ SIPEnhancedTransaction,
84
+ TransactionSummary,
85
+ } from './helius-enhanced-types'
@@ -27,6 +27,9 @@
27
27
 
28
28
  import { HeliusProvider, type HeliusProviderConfig } from './helius'
29
29
  import { GenericProvider } from './generic'
30
+ import { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
31
+ import { TritonProvider, type TritonProviderConfig } from './triton'
32
+ import { ValidationError, ErrorCode } from '../../../errors'
30
33
 
31
34
  /**
32
35
  * Token asset information returned by providers
@@ -129,50 +132,90 @@ export interface GenericProviderConfig extends ProviderConfig {
129
132
  *
130
133
  * @example
131
134
  * ```typescript
132
- * // Helius with API key
135
+ * // Helius with DAS API (recommended for production)
133
136
  * const helius = createProvider('helius', {
134
137
  * apiKey: process.env.HELIUS_API_KEY,
135
138
  * cluster: 'devnet'
136
139
  * })
137
140
  *
138
- * // Generic with existing connection
139
- * const generic = createProvider('generic', { connection })
141
+ * // QuickNode with Yellowstone gRPC (real-time subscriptions)
142
+ * const quicknode = createProvider('quicknode', {
143
+ * endpoint: process.env.QUICKNODE_ENDPOINT
144
+ * })
140
145
  *
141
- * // Generic with endpoint
142
- * const devnet = createProvider('generic', {
143
- * endpoint: 'https://api.devnet.solana.com'
146
+ * // Triton with Dragon's Mouth gRPC (ultra-low latency)
147
+ * const triton = createProvider('triton', {
148
+ * xToken: process.env.TRITON_TOKEN
144
149
  * })
150
+ *
151
+ * // Generic with existing connection
152
+ * const generic = createProvider('generic', { connection })
145
153
  * ```
146
154
  */
147
155
  export function createProvider(
148
156
  type: 'helius',
149
157
  config: ProviderConfig & { apiKey: string }
150
158
  ): SolanaRPCProvider
159
+ export function createProvider(
160
+ type: 'quicknode',
161
+ config: QuickNodeProviderConfig
162
+ ): SolanaRPCProvider
163
+ export function createProvider(
164
+ type: 'triton',
165
+ config: TritonProviderConfig
166
+ ): SolanaRPCProvider
151
167
  export function createProvider(
152
168
  type: 'generic',
153
169
  config: GenericProviderConfig
154
170
  ): SolanaRPCProvider
155
171
  export function createProvider(
156
172
  type: ProviderType,
157
- config: ProviderConfig | GenericProviderConfig
173
+ config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
158
174
  ): SolanaRPCProvider
159
175
  export function createProvider(
160
176
  type: ProviderType,
161
- config: ProviderConfig | GenericProviderConfig
177
+ config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
162
178
  ): SolanaRPCProvider {
179
+ // Validate config before type casting
180
+ if (!config || typeof config !== 'object') {
181
+ throw new ValidationError('Provider config is required', 'config')
182
+ }
183
+
163
184
  switch (type) {
164
- case 'helius':
165
- return new HeliusProvider(config as HeliusProviderConfig)
166
- case 'generic':
167
- return new GenericProvider(config as GenericProviderConfig)
185
+ case 'helius': {
186
+ // Validate required fields for HeliusProvider
187
+ const heliusConfig = config as HeliusProviderConfig
188
+ if (!heliusConfig.apiKey || typeof heliusConfig.apiKey !== 'string') {
189
+ throw new ValidationError(
190
+ 'Helius provider requires an API key',
191
+ 'apiKey'
192
+ )
193
+ }
194
+ if (heliusConfig.cluster && !['mainnet-beta', 'devnet'].includes(heliusConfig.cluster)) {
195
+ throw new ValidationError(
196
+ 'Invalid cluster. Must be "mainnet-beta" or "devnet"',
197
+ 'cluster'
198
+ )
199
+ }
200
+ return new HeliusProvider(heliusConfig)
201
+ }
168
202
  case 'quicknode':
203
+ return new QuickNodeProvider(config as QuickNodeProviderConfig)
169
204
  case 'triton':
170
- throw new Error(
171
- `Provider '${type}' is not yet implemented. ` +
172
- `Use 'helius' or 'generic' for now. ` +
173
- `See https://github.com/sip-protocol/sip-protocol/issues/${type === 'quicknode' ? '494' : '495'}`
174
- )
205
+ return new TritonProvider(config as TritonProviderConfig)
206
+ case 'generic': {
207
+ // Validate GenericProvider config
208
+ const genericConfig = config as GenericProviderConfig
209
+ // Must have either connection, endpoint, or cluster
210
+ if (!genericConfig.connection && !genericConfig.endpoint && !genericConfig.cluster) {
211
+ throw new ValidationError(
212
+ 'Generic provider requires either connection, endpoint, or cluster',
213
+ 'config'
214
+ )
215
+ }
216
+ return new GenericProvider(genericConfig)
217
+ }
175
218
  default:
176
- throw new Error(`Unknown provider type: ${type}`)
219
+ throw new ValidationError(`unknown provider type: ${type}`, 'type', undefined, ErrorCode.INVALID_INPUT)
177
220
  }
178
221
  }