@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,402 @@
1
+ /**
2
+ * Helius RPC Provider
3
+ *
4
+ * Leverages Helius DAS (Digital Asset Standard) API for efficient
5
+ * token balance queries and asset metadata.
6
+ *
7
+ * @see https://docs.helius.dev/solana-apis/digital-asset-standard-das-api
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { HeliusProvider } from '@sip-protocol/sdk'
12
+ *
13
+ * const helius = new HeliusProvider({
14
+ * apiKey: process.env.HELIUS_API_KEY!,
15
+ * cluster: 'devnet'
16
+ * })
17
+ *
18
+ * const assets = await helius.getAssetsByOwner('7xK9...')
19
+ * console.log(assets) // [{ mint: '...', amount: 1000000n, decimals: 6, symbol: 'USDC' }]
20
+ * ```
21
+ */
22
+
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
+ }
91
+
92
+ /**
93
+ * Helius API response types
94
+ */
95
+ interface HeliusDASAsset {
96
+ id: string
97
+ interface: string
98
+ content?: {
99
+ metadata?: {
100
+ name?: string
101
+ symbol?: string
102
+ }
103
+ links?: {
104
+ image?: string
105
+ }
106
+ }
107
+ token_info?: {
108
+ /** Balance as string to preserve precision for large values */
109
+ balance?: string | number
110
+ decimals?: number
111
+ symbol?: string
112
+ token_program?: string
113
+ mint_authority?: string
114
+ freeze_authority?: string
115
+ }
116
+ ownership?: {
117
+ owner: string
118
+ }
119
+ }
120
+
121
+ interface HeliusDASResponse {
122
+ jsonrpc: string
123
+ result?: {
124
+ items: HeliusDASAsset[]
125
+ total: number
126
+ limit: number
127
+ page: number
128
+ cursor?: string
129
+ }
130
+ error?: {
131
+ code: number
132
+ message: string
133
+ }
134
+ id: string
135
+ }
136
+
137
+ interface HeliusBalancesResponse {
138
+ tokens: Array<{
139
+ mint: string
140
+ amount: number
141
+ decimals: number
142
+ tokenAccount: string
143
+ }>
144
+ nativeBalance: number
145
+ }
146
+
147
+ /**
148
+ * Helius provider configuration
149
+ *
150
+ * @security API keys should be treated as sensitive credentials.
151
+ */
152
+ export interface HeliusProviderConfig extends ProviderConfig {
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
+ */
160
+ apiKey: string
161
+ /** Solana cluster (default: mainnet-beta) */
162
+ cluster?: 'mainnet-beta' | 'devnet'
163
+ }
164
+
165
+ /**
166
+ * Helius RPC Provider implementation
167
+ *
168
+ * Uses Helius DAS API for efficient token queries.
169
+ * Recommended for production deployments.
170
+ */
171
+ export class HeliusProvider implements SolanaRPCProvider {
172
+ readonly name = 'helius'
173
+ private readonly apiKey: string
174
+ private readonly cluster: 'mainnet-beta' | 'devnet'
175
+ private readonly rpcUrl: string
176
+ private readonly restUrl: string
177
+
178
+ constructor(config: HeliusProviderConfig) {
179
+ // Validate API key
180
+ if (!config.apiKey) {
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
+ )
193
+ }
194
+
195
+ this.apiKey = config.apiKey
196
+ this.cluster = config.cluster ?? 'mainnet-beta'
197
+
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
200
+ this.rpcUrl = this.cluster === 'devnet'
201
+ ? 'https://devnet.helius-rpc.com'
202
+ : 'https://mainnet.helius-rpc.com'
203
+
204
+ // REST endpoint for balances API
205
+ this.restUrl = this.cluster === 'devnet'
206
+ ? 'https://api-devnet.helius.xyz/v0'
207
+ : 'https://api.helius.xyz/v0'
208
+ }
209
+
210
+ /**
211
+ * Get all token assets owned by an address using DAS API
212
+ *
213
+ * Uses getAssetsByOwner for comprehensive asset information including
214
+ * NFTs and fungible tokens with metadata.
215
+ */
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
+
226
+ const assets: TokenAsset[] = []
227
+ let page = 1
228
+ const limit = HELIUS_DAS_PAGE_LIMIT
229
+ let hasMore = true
230
+
231
+ while (hasMore) {
232
+ const response = await fetchWithTimeout(this.rpcUrl, {
233
+ method: 'POST',
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
+ },
239
+ body: JSON.stringify({
240
+ jsonrpc: '2.0',
241
+ id: `sip-${Date.now()}`,
242
+ method: 'getAssetsByOwner',
243
+ params: {
244
+ ownerAddress: owner,
245
+ page,
246
+ limit,
247
+ displayOptions: {
248
+ showFungible: true,
249
+ showNativeBalance: false,
250
+ },
251
+ },
252
+ }),
253
+ })
254
+
255
+ if (!response.ok) {
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
+ )
262
+ }
263
+
264
+ const data = (await response.json()) as HeliusDASResponse
265
+
266
+ // Handle JSON-RPC errors
267
+ if (data.error) {
268
+ throw new NetworkError(
269
+ `Helius RPC error: ${data.error.message} (code: ${data.error.code})`,
270
+ undefined,
271
+ { endpoint: sanitizeUrl(this.rpcUrl) }
272
+ )
273
+ }
274
+
275
+ if (data.result?.items) {
276
+ for (const item of data.result.items) {
277
+ // Skip NFTs (interface !== 'FungibleToken' and 'FungibleAsset')
278
+ if (item.interface !== 'FungibleToken' && item.interface !== 'FungibleAsset') {
279
+ continue
280
+ }
281
+
282
+ // Extract token info
283
+ const tokenInfo = item.token_info
284
+ if (!tokenInfo?.balance) continue
285
+
286
+ // Convert balance to BigInt, handling both string and number types
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
+ }
295
+
296
+ assets.push({
297
+ mint: item.id,
298
+ amount: balanceValue,
299
+ decimals: tokenInfo.decimals ?? 0,
300
+ symbol: tokenInfo.symbol ?? item.content?.metadata?.symbol,
301
+ name: item.content?.metadata?.name,
302
+ logoUri: item.content?.links?.image,
303
+ })
304
+ }
305
+ }
306
+
307
+ // Check if there are more pages
308
+ hasMore = data.result?.items?.length === limit
309
+ page++
310
+
311
+ // Safety limit to prevent infinite loops
312
+ if (page > HELIUS_MAX_PAGES) {
313
+ break
314
+ }
315
+ }
316
+
317
+ return assets
318
+ }
319
+
320
+ /**
321
+ * Get token balance for a specific mint using Balances API
322
+ *
323
+ * More efficient than getAssetsByOwner when you only need one token's balance.
324
+ */
325
+ async getTokenBalance(owner: string, mint: string): Promise<bigint> {
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
+ }
340
+
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
+ })
349
+
350
+ if (!response.ok) {
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
+ )
363
+ }
364
+
365
+ const data = (await response.json()) as HeliusBalancesResponse
366
+
367
+ const token = data.tokens?.find((t) => t.mint === mint)
368
+ return token ? BigInt(token.amount) : 0n
369
+ } catch (error) {
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
376
+ }
377
+ }
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
+
390
+ /**
391
+ * Check if provider supports real-time subscriptions
392
+ *
393
+ * Helius supports webhooks for real-time notifications,
394
+ * but that requires server-side setup. Client-side subscriptions
395
+ * are not directly supported.
396
+ */
397
+ supportsSubscriptions(): boolean {
398
+ // Helius has webhooks but not client-side subscriptions
399
+ // Return false for now, can be enhanced with webhook integration later
400
+ return false
401
+ }
402
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Solana RPC Providers
3
+ *
4
+ * SIP is RPC-provider-agnostic — developers choose their preferred provider.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createProvider, scanForPayments } from '@sip-protocol/sdk'
9
+ *
10
+ * // Helius — efficient DAS queries (recommended for production)
11
+ * const helius = createProvider('helius', {
12
+ * apiKey: process.env.HELIUS_API_KEY!
13
+ * })
14
+ *
15
+ * // Generic — standard RPC, no API key needed
16
+ * const generic = createProvider('generic', {
17
+ * endpoint: 'https://api.devnet.solana.com'
18
+ * })
19
+ *
20
+ * // Same API, different backends
21
+ * const payments = await scanForPayments({
22
+ * provider: helius,
23
+ * viewingPrivateKey,
24
+ * spendingPublicKey,
25
+ * })
26
+ * ```
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+
31
+ // Interface and factory
32
+ export {
33
+ createProvider,
34
+ type SolanaRPCProvider,
35
+ type TokenAsset,
36
+ type ProviderConfig,
37
+ type ProviderType,
38
+ type GenericProviderConfig,
39
+ } from './interface'
40
+
41
+ // Provider implementations
42
+ export { HeliusProvider, type HeliusProviderConfig } from './helius'
43
+ export { GenericProvider } from './generic'
44
+ export { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
45
+ export { TritonProvider, type TritonProviderConfig } from './triton'
46
+
47
+ // Webhook handler for real-time scanning
48
+ export {
49
+ createWebhookHandler,
50
+ processWebhookTransaction,
51
+ verifyWebhookSignature,
52
+ verifyAuthToken,
53
+ type HeliusWebhookTransaction,
54
+ type HeliusEnhancedTransaction,
55
+ type HeliusWebhookPayload,
56
+ type WebhookHandlerConfig,
57
+ type WebhookProcessResult,
58
+ type WebhookRequest,
59
+ type WebhookHandler,
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'
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Solana RPC Provider Interface
3
+ *
4
+ * SIP is RPC-provider-agnostic — developers choose their preferred provider.
5
+ * Each provider has unique moats we leverage through this unified interface.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createProvider, scanForPayments } from '@sip-protocol/sdk'
10
+ *
11
+ * // Helius — efficient DAS queries (recommended for production)
12
+ * const helius = createProvider('helius', { apiKey: process.env.HELIUS_API_KEY })
13
+ *
14
+ * // Generic — standard RPC, no API key needed
15
+ * const generic = createProvider('generic', { connection })
16
+ *
17
+ * // Same API, different backends
18
+ * const payments = await scanForPayments({
19
+ * provider: helius,
20
+ * viewingPrivateKey,
21
+ * spendingPublicKey,
22
+ * })
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+
28
+ import { HeliusProvider, type HeliusProviderConfig } from './helius'
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'
33
+
34
+ /**
35
+ * Token asset information returned by providers
36
+ */
37
+ export interface TokenAsset {
38
+ /** SPL token mint address */
39
+ mint: string
40
+ /** Token amount in smallest units */
41
+ amount: bigint
42
+ /** Token decimals */
43
+ decimals: number
44
+ /** Token symbol (e.g., 'USDC') */
45
+ symbol?: string
46
+ /** Token name (e.g., 'USD Coin') */
47
+ name?: string
48
+ /** Token logo URI */
49
+ logoUri?: string
50
+ }
51
+
52
+ /**
53
+ * Configuration for RPC providers
54
+ */
55
+ export interface ProviderConfig {
56
+ /** API key for premium providers (Helius, QuickNode) */
57
+ apiKey?: string
58
+ /** Custom RPC endpoint */
59
+ endpoint?: string
60
+ /** Solana cluster */
61
+ cluster?: 'mainnet-beta' | 'devnet' | 'testnet'
62
+ }
63
+
64
+ /**
65
+ * Unified interface for Solana RPC providers
66
+ *
67
+ * All provider adapters must implement this interface to ensure
68
+ * consistent behavior across different RPC backends.
69
+ */
70
+ export interface SolanaRPCProvider {
71
+ /** Provider name for logging/debugging */
72
+ readonly name: string
73
+
74
+ /**
75
+ * Get all token assets owned by an address
76
+ *
77
+ * @param owner - Solana address (base58)
78
+ * @returns Array of token assets with balances
79
+ */
80
+ getAssetsByOwner(owner: string): Promise<TokenAsset[]>
81
+
82
+ /**
83
+ * Get token balance for a specific mint
84
+ *
85
+ * @param owner - Solana address (base58)
86
+ * @param mint - SPL token mint address (base58)
87
+ * @returns Token balance in smallest units, 0n if no balance
88
+ */
89
+ getTokenBalance(owner: string, mint: string): Promise<bigint>
90
+
91
+ /**
92
+ * Check if provider supports real-time subscriptions
93
+ *
94
+ * @returns true if subscribeToTransfers is available
95
+ */
96
+ supportsSubscriptions(): boolean
97
+
98
+ /**
99
+ * Subscribe to token transfers for an address (optional)
100
+ *
101
+ * Only available if supportsSubscriptions() returns true.
102
+ *
103
+ * @param address - Solana address to watch
104
+ * @param callback - Called when a transfer is detected
105
+ * @returns Unsubscribe function
106
+ */
107
+ subscribeToTransfers?(
108
+ address: string,
109
+ callback: (asset: TokenAsset) => void
110
+ ): Promise<() => void>
111
+ }
112
+
113
+ /**
114
+ * Provider type for factory function
115
+ */
116
+ export type ProviderType = 'helius' | 'quicknode' | 'triton' | 'generic'
117
+
118
+ /**
119
+ * Extended config for generic provider that accepts a Connection
120
+ */
121
+ export interface GenericProviderConfig extends ProviderConfig {
122
+ /** Existing Solana Connection object */
123
+ connection?: unknown // Typed as unknown to avoid @solana/web3.js dependency in interface
124
+ }
125
+
126
+ /**
127
+ * Create an RPC provider instance
128
+ *
129
+ * @param type - Provider type ('helius', 'quicknode', 'triton', 'generic')
130
+ * @param config - Provider configuration
131
+ * @returns Configured provider instance
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // Helius with DAS API (recommended for production)
136
+ * const helius = createProvider('helius', {
137
+ * apiKey: process.env.HELIUS_API_KEY,
138
+ * cluster: 'devnet'
139
+ * })
140
+ *
141
+ * // QuickNode with Yellowstone gRPC (real-time subscriptions)
142
+ * const quicknode = createProvider('quicknode', {
143
+ * endpoint: process.env.QUICKNODE_ENDPOINT
144
+ * })
145
+ *
146
+ * // Triton with Dragon's Mouth gRPC (ultra-low latency)
147
+ * const triton = createProvider('triton', {
148
+ * xToken: process.env.TRITON_TOKEN
149
+ * })
150
+ *
151
+ * // Generic with existing connection
152
+ * const generic = createProvider('generic', { connection })
153
+ * ```
154
+ */
155
+ export function createProvider(
156
+ type: 'helius',
157
+ config: ProviderConfig & { apiKey: string }
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
167
+ export function createProvider(
168
+ type: 'generic',
169
+ config: GenericProviderConfig
170
+ ): SolanaRPCProvider
171
+ export function createProvider(
172
+ type: ProviderType,
173
+ config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
174
+ ): SolanaRPCProvider
175
+ export function createProvider(
176
+ type: ProviderType,
177
+ config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
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
+
184
+ switch (type) {
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
+ }
202
+ case 'quicknode':
203
+ return new QuickNodeProvider(config as QuickNodeProviderConfig)
204
+ case 'triton':
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
+ }
218
+ default:
219
+ throw new ValidationError(`unknown provider type: ${type}`, 'type', undefined, ErrorCode.INVALID_INPUT)
220
+ }
221
+ }