@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
@@ -13,7 +13,6 @@ import {
13
13
  getAssociatedTokenAddress,
14
14
  createTransferInstruction,
15
15
  getAccount,
16
- TOKEN_PROGRAM_ID,
17
16
  } from '@solana/spl-token'
18
17
  import {
19
18
  checkEd25519StealthAddress,
@@ -32,10 +31,14 @@ import {
32
31
  SIP_MEMO_PREFIX,
33
32
  MEMO_PROGRAM_ID,
34
33
  getExplorerUrl,
35
- SOLANA_TOKEN_MINTS,
34
+ DEFAULT_SCAN_LIMIT,
35
+ VIEW_TAG_MAX,
36
36
  type SolanaCluster,
37
37
  } from './constants'
38
- import { hexToBytes, bytesToHex } from '@noble/hashes/utils'
38
+ import { getTokenSymbol, parseTokenTransferFromBalances } from './utils'
39
+ import type { SolanaRPCProvider } from './providers/interface'
40
+ import { hexToBytes } from '@noble/hashes/utils'
41
+ import { ed25519 } from '@noble/curves/ed25519'
39
42
 
40
43
  /**
41
44
  * Scan for incoming stealth payments
@@ -69,7 +72,8 @@ export async function scanForPayments(
69
72
  spendingPublicKey,
70
73
  fromSlot,
71
74
  toSlot,
72
- limit = 100,
75
+ limit = DEFAULT_SCAN_LIMIT,
76
+ provider,
73
77
  } = params
74
78
 
75
79
  const results: SolanaScanResult[] = []
@@ -117,39 +121,88 @@ export async function scanForPayments(
117
121
  const announcement = parseAnnouncement(memoContent)
118
122
  if (!announcement) continue
119
123
 
120
- // Check if this payment is for us using view tag first
121
- const ephemeralPubKeyHex = solanaAddressToEd25519PublicKey(
122
- announcement.ephemeralPublicKey
123
- )
124
+ // M5 FIX: Wrap ephemeral key conversion in try-catch
125
+ let ephemeralPubKeyHex: HexString
126
+ try {
127
+ ephemeralPubKeyHex = solanaAddressToEd25519PublicKey(
128
+ announcement.ephemeralPublicKey
129
+ )
130
+ } catch {
131
+ // Invalid ephemeral key format, skip this announcement
132
+ continue
133
+ }
124
134
 
125
135
  // Construct stealth address object for checking
126
136
  // viewTag is a number (0-255), parse from hex string
127
137
  const viewTagNumber = parseInt(announcement.viewTag, 16)
128
- const stealthAddressToCheck: StealthAddress = {
129
- address: announcement.stealthAddress
138
+
139
+ // M5 FIX: Validate view tag range
140
+ if (!Number.isInteger(viewTagNumber) || viewTagNumber < 0 || viewTagNumber > VIEW_TAG_MAX) {
141
+ continue
142
+ }
143
+
144
+ let stealthAddressHex: HexString
145
+ try {
146
+ stealthAddressHex = announcement.stealthAddress
130
147
  ? solanaAddressToEd25519PublicKey(announcement.stealthAddress)
131
- : ('0x' + '00'.repeat(32)) as HexString, // Will be computed
148
+ : ('0x' + '00'.repeat(32)) as HexString
149
+ } catch {
150
+ continue
151
+ }
152
+
153
+ const stealthAddressToCheck: StealthAddress = {
154
+ address: stealthAddressHex,
132
155
  ephemeralPublicKey: ephemeralPubKeyHex,
133
156
  viewTag: viewTagNumber,
134
157
  }
135
158
 
136
- // Check if this is our payment
137
- const isOurs = checkEd25519StealthAddress(
138
- stealthAddressToCheck,
139
- viewingPrivateKey,
140
- spendingPublicKey
141
- )
159
+ // M5 FIX: Wrap checkEd25519StealthAddress in try-catch
160
+ // This can throw for invalid curve points
161
+ let isOurs = false
162
+ try {
163
+ isOurs = checkEd25519StealthAddress(
164
+ stealthAddressToCheck,
165
+ viewingPrivateKey,
166
+ spendingPublicKey
167
+ )
168
+ } catch {
169
+ // Invalid keys or malformed data - not our payment
170
+ continue
171
+ }
142
172
 
143
173
  if (isOurs) {
144
- // Parse token transfer from transaction
145
- const transferInfo = parseTokenTransfer(tx)
174
+ // Parse token transfer from transaction using shared utility
175
+ const transferInfo = parseTokenTransferFromBalances(
176
+ tx?.meta?.preTokenBalances as Parameters<typeof parseTokenTransferFromBalances>[0],
177
+ tx?.meta?.postTokenBalances as Parameters<typeof parseTokenTransferFromBalances>[1]
178
+ )
146
179
  if (transferInfo) {
180
+ // If provider is available, use it for more accurate current balance
181
+ let amount = transferInfo.amount
182
+ const tokenSymbol = getTokenSymbol(transferInfo.mint)
183
+
184
+ if (provider && announcement.stealthAddress) {
185
+ try {
186
+ // Use getTokenBalance for efficient single-token query
187
+ const balance = await provider.getTokenBalance(
188
+ announcement.stealthAddress,
189
+ transferInfo.mint
190
+ )
191
+ // Only use provider balance if > 0 (confirms tokens still there)
192
+ if (balance > 0n) {
193
+ amount = balance
194
+ }
195
+ } catch {
196
+ // Fallback to parsed transfer info if provider fails
197
+ }
198
+ }
199
+
147
200
  results.push({
148
201
  stealthAddress: announcement.stealthAddress || '',
149
202
  ephemeralPublicKey: announcement.ephemeralPublicKey,
150
- amount: transferInfo.amount,
203
+ amount,
151
204
  mint: transferInfo.mint,
152
- tokenSymbol: getTokenSymbol(transferInfo.mint),
205
+ tokenSymbol,
153
206
  txSignature: sigInfo.signature,
154
207
  slot: sigInfo.slot,
155
208
  timestamp: sigInfo.blockTime || 0,
@@ -157,14 +210,15 @@ export async function scanForPayments(
157
210
  }
158
211
  }
159
212
  }
160
- } catch (err) {
161
- // Skip failed transaction parsing
162
- console.warn(`Failed to parse tx ${sigInfo.signature}:`, err)
213
+ } catch {
214
+ // M10 FIX: Skip failed transaction parsing silently
215
+ // Individual tx parse failures shouldn't block scanning
163
216
  }
164
217
  }
165
218
  } catch (err) {
166
- console.error('Scan failed:', err)
167
- throw new Error(`Failed to scan for payments: ${err}`)
219
+ // M10 FIX: Remove console.error, throw proper error
220
+ const message = err instanceof Error ? err.message : String(err)
221
+ throw new Error(`Failed to scan for payments: ${message}`)
168
222
  }
169
223
 
170
224
  return results
@@ -193,6 +247,9 @@ export async function scanForPayments(
193
247
  * console.log('Claimed! Tx:', result.txSignature)
194
248
  * ```
195
249
  */
250
+ /** Minimum SOL balance required for transaction fees (in lamports) */
251
+ const MIN_SOL_FOR_FEES = 5000n // ~0.000005 SOL, typical tx fee
252
+
196
253
  export async function claimStealthPayment(
197
254
  params: SolanaClaimParams
198
255
  ): Promise<SolanaClaimResult> {
@@ -206,6 +263,17 @@ export async function claimStealthPayment(
206
263
  mint,
207
264
  } = params
208
265
 
266
+ // M7 FIX: Check SOL balance for fees before attempting claim
267
+ const stealthPubkeyForBalance = new PublicKey(stealthAddress)
268
+ const solBalance = await connection.getBalance(stealthPubkeyForBalance)
269
+ if (BigInt(solBalance) < MIN_SOL_FOR_FEES) {
270
+ throw new Error(
271
+ `Insufficient SOL for transaction fees. Stealth address has ${solBalance} lamports, ` +
272
+ `needs at least ${MIN_SOL_FOR_FEES} lamports (~0.000005 SOL). ` +
273
+ `Fund the stealth address with SOL before claiming.`
274
+ )
275
+ }
276
+
209
277
  // Convert addresses to hex for SDK functions
210
278
  const stealthAddressHex = solanaAddressToEd25519PublicKey(stealthAddress)
211
279
  const ephemeralPubKeyHex = solanaAddressToEd25519PublicKey(ephemeralPublicKey)
@@ -229,11 +297,31 @@ export async function claimStealthPayment(
229
297
  // The SDK returns a scalar, so we need to handle this carefully
230
298
  const stealthPrivKeyBytes = hexToBytes(recovery.privateKey.slice(2))
231
299
 
232
- // Solana keypairs expect 64 bytes (32 byte seed + 32 byte public key)
233
- // We construct this from the derived scalar
300
+ // Validate that the derived private key (scalar) produces the expected public key
301
+ // Note: SIP derives a scalar, not a seed. We use scalar multiplication to verify.
234
302
  const stealthPubkey = new PublicKey(stealthAddress)
303
+ const expectedPubKeyBytes = stealthPubkey.toBytes()
304
+
305
+ // Convert scalar bytes to bigint (little-endian for ed25519)
306
+ const scalarBigInt = bytesToBigIntLE(stealthPrivKeyBytes)
307
+ const ED25519_ORDER = 2n ** 252n + 27742317777372353535851937790883648493n
308
+ let validScalar = scalarBigInt % ED25519_ORDER
309
+ if (validScalar === 0n) validScalar = 1n
310
+
311
+ // Derive public key via scalar multiplication
312
+ const derivedPubKeyBytes = ed25519.ExtendedPoint.BASE.multiply(validScalar).toRawBytes()
313
+
314
+ if (!derivedPubKeyBytes.every((b, i) => b === expectedPubKeyBytes[i])) {
315
+ throw new Error(
316
+ 'Stealth key derivation failed: derived private key does not produce expected public key. ' +
317
+ 'This may indicate incorrect spending/viewing keys or corrupted announcement data.'
318
+ )
319
+ }
320
+
321
+ // Solana keypairs expect 64 bytes (32 byte seed + 32 byte public key)
322
+ // We construct this from the derived scalar (now validated)
235
323
  const stealthKeypair = Keypair.fromSecretKey(
236
- new Uint8Array([...stealthPrivKeyBytes, ...stealthPubkey.toBytes()])
324
+ new Uint8Array([...stealthPrivKeyBytes, ...expectedPubKeyBytes])
237
325
  )
238
326
 
239
327
  // Get token accounts
@@ -306,12 +394,39 @@ export async function claimStealthPayment(
306
394
 
307
395
  /**
308
396
  * Get token balance for a stealth address
397
+ *
398
+ * @param connection - Solana RPC connection
399
+ * @param stealthAddress - Stealth address to check (base58)
400
+ * @param mint - SPL token mint address
401
+ * @param provider - Optional RPC provider for efficient queries
402
+ * @returns Token balance in smallest unit
403
+ *
404
+ * @example
405
+ * ```typescript
406
+ * // Using standard RPC
407
+ * const balance = await getStealthBalance(connection, stealthAddr, mint)
408
+ *
409
+ * // Using Helius for efficient queries
410
+ * const helius = createProvider('helius', { apiKey })
411
+ * const balance = await getStealthBalance(connection, stealthAddr, mint, helius)
412
+ * ```
309
413
  */
310
414
  export async function getStealthBalance(
311
415
  connection: SolanaScanParams['connection'],
312
416
  stealthAddress: string,
313
- mint: PublicKey
417
+ mint: PublicKey,
418
+ provider?: SolanaRPCProvider
314
419
  ): Promise<bigint> {
420
+ // Use provider if available for efficient queries
421
+ if (provider) {
422
+ try {
423
+ return await provider.getTokenBalance(stealthAddress, mint.toBase58())
424
+ } catch {
425
+ // Fallback to standard RPC if provider fails
426
+ }
427
+ }
428
+
429
+ // Standard RPC fallback
315
430
  try {
316
431
  const stealthPubkey = new PublicKey(stealthAddress)
317
432
  const ata = await getAssociatedTokenAddress(mint, stealthPubkey, true)
@@ -322,51 +437,16 @@ export async function getStealthBalance(
322
437
  }
323
438
  }
324
439
 
325
- /**
326
- * Parse token transfer info from a transaction
327
- */
328
- function parseTokenTransfer(
329
- tx: Awaited<ReturnType<typeof import('@solana/web3.js').Connection.prototype.getTransaction>>
330
- ): { mint: string; amount: bigint } | null {
331
- if (!tx?.meta?.postTokenBalances || !tx.meta.preTokenBalances) {
332
- return null
333
- }
334
-
335
- // Find token balance changes
336
- for (let i = 0; i < tx.meta.postTokenBalances.length; i++) {
337
- const post = tx.meta.postTokenBalances[i]
338
- const pre = tx.meta.preTokenBalances.find(
339
- (p) => p.accountIndex === post.accountIndex
340
- )
341
-
342
- const postAmount = BigInt(post.uiTokenAmount.amount)
343
- const preAmount = pre ? BigInt(pre.uiTokenAmount.amount) : 0n
344
-
345
- if (postAmount > preAmount) {
346
- return {
347
- mint: post.mint,
348
- amount: postAmount - preAmount,
349
- }
350
- }
351
- }
352
-
353
- return null
354
- }
440
+ // Token transfer parsing and symbol lookup moved to ./utils.ts (L3 fix)
355
441
 
356
442
  /**
357
- * Get token symbol from mint address
358
- */
359
- function getTokenSymbol(mint: string): string | undefined {
360
- for (const [symbol, address] of Object.entries(SOLANA_TOKEN_MINTS)) {
361
- if (address === mint) {
362
- return symbol
363
- }
364
- }
365
- return undefined
366
- }
367
-
368
- /**
369
- * Detect Solana cluster from RPC endpoint
443
+ * Detect Solana cluster from RPC endpoint URL
444
+ *
445
+ * Parses the endpoint URL to determine which Solana cluster it connects to.
446
+ *
447
+ * @param endpoint - RPC endpoint URL
448
+ * @returns Detected cluster name
449
+ * @internal
370
450
  */
371
451
  function detectCluster(endpoint: string): SolanaCluster {
372
452
  if (endpoint.includes('devnet')) {
@@ -380,3 +460,20 @@ function detectCluster(endpoint: string): SolanaCluster {
380
460
  }
381
461
  return 'mainnet-beta'
382
462
  }
463
+
464
+ /**
465
+ * Convert bytes to bigint in little-endian format
466
+ *
467
+ * Used for ed25519 scalar conversion where bytes are in little-endian order.
468
+ *
469
+ * @param bytes - Byte array to convert
470
+ * @returns BigInt representation of the bytes
471
+ * @internal
472
+ */
473
+ function bytesToBigIntLE(bytes: Uint8Array): bigint {
474
+ let result = 0n
475
+ for (let i = bytes.length - 1; i >= 0; i--) {
476
+ result = (result << 8n) | BigInt(bytes[i])
477
+ }
478
+ return result
479
+ }