@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,577 @@
1
+ /**
2
+ * Solana Pedersen Commitment Implementation
3
+ *
4
+ * Ed25519-based Pedersen commitments for SPL token privacy on Solana.
5
+ *
6
+ * ## Security Properties
7
+ *
8
+ * - **Hiding (Computational)**: Cannot determine value from commitment
9
+ * - **Binding (Computational)**: Cannot open commitment to different value
10
+ * - **Homomorphic**: C(v1) + C(v2) = C(v1 + v2) when blindings sum
11
+ *
12
+ * ## Ed25519 vs Secp256k1
13
+ *
14
+ * This implementation uses ed25519 (Curve25519 in Edwards form) for
15
+ * compatibility with Solana's native cryptography. The core SDK uses
16
+ * secp256k1 for EVM compatibility.
17
+ *
18
+ * @module chains/solana/commitment
19
+ */
20
+
21
+ import { ed25519 } from '@noble/curves/ed25519'
22
+ import { sha256 } from '@noble/hashes/sha2'
23
+ import { bytesToHex, randomBytes } from '@noble/hashes/utils'
24
+ import type { HexString } from '@sip-protocol/types'
25
+ import { ValidationError, CryptoError, ErrorCode } from '../../errors'
26
+ import { isValidHex } from '../../validation'
27
+
28
+ // ─── Types ────────────────────────────────────────────────────────────────────
29
+
30
+ /**
31
+ * A Pedersen commitment with associated blinding factor
32
+ */
33
+ export interface SolanaPedersenCommitment {
34
+ /**
35
+ * The commitment point C = v*G + r*H (compressed ed25519, 32 bytes)
36
+ */
37
+ commitment: HexString
38
+
39
+ /**
40
+ * The blinding factor r (32 bytes, secret)
41
+ * Required to open/verify the commitment
42
+ */
43
+ blinding: HexString
44
+ }
45
+
46
+ /**
47
+ * A commitment point without the blinding factor (for public sharing)
48
+ */
49
+ export interface SolanaCommitmentPoint {
50
+ /**
51
+ * The commitment point (compressed ed25519, 32 bytes)
52
+ */
53
+ commitment: HexString
54
+ }
55
+
56
+ /**
57
+ * SPL token commitment with amount and token info
58
+ */
59
+ export interface SPLTokenCommitment extends SolanaPedersenCommitment {
60
+ /**
61
+ * SPL token mint address
62
+ */
63
+ mint: string
64
+
65
+ /**
66
+ * Token decimals (for display/calculation)
67
+ */
68
+ decimals: number
69
+
70
+ /**
71
+ * Original amount in smallest units (u64)
72
+ */
73
+ amountRaw?: bigint
74
+ }
75
+
76
+ // ─── Constants ────────────────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * Domain separation tag for H generation (ed25519 version)
80
+ */
81
+ const H_DOMAIN = 'SIP-SOLANA-PEDERSEN-GENERATOR-H-v1'
82
+
83
+ /**
84
+ * The generator G (ed25519 base point)
85
+ */
86
+ const G = ed25519.ExtendedPoint.BASE
87
+
88
+ /**
89
+ * The ed25519 curve order L (number of points in the subgroup)
90
+ */
91
+ export const ED25519_ORDER = 2n ** 252n + 27742317777372353535851937790883648493n
92
+
93
+ /**
94
+ * Ed25519 cofactor (the curve has order 8*L where L is the prime subgroup order)
95
+ * We multiply hash-derived points by the cofactor to ensure they're in the
96
+ * prime-order subgroup, which is required for correct homomorphic properties.
97
+ */
98
+ const ED25519_COFACTOR = 8n
99
+
100
+ /**
101
+ * Maximum SPL token amount (u64::MAX)
102
+ */
103
+ export const MAX_SPL_AMOUNT = 2n ** 64n - 1n
104
+
105
+ // ─── Generator H Construction ─────────────────────────────────────────────────
106
+
107
+ /**
108
+ * The independent generator H (NUMS point)
109
+ * Constructed via hash-to-curve with nothing-up-my-sleeve string.
110
+ * IMPORTANT: Must be defined after ED25519_COFACTOR for proper initialization order.
111
+ */
112
+ const H = generateH()
113
+
114
+ /**
115
+ * Generate the independent generator H using NUMS method for ed25519
116
+ *
117
+ * We use a simple hash-and-check approach:
118
+ * 1. Hash domain||counter to get 32 bytes
119
+ * 2. Try to decode as an ed25519 point
120
+ * 3. Clear the cofactor by multiplying by 8 (ensures point is in prime-order subgroup)
121
+ * 4. If valid and not identity/G, use it
122
+ *
123
+ * This ensures nobody knows the discrete log of H w.r.t. G.
124
+ *
125
+ * IMPORTANT: The cofactor clearing step is essential for Pedersen commitments.
126
+ * Without it, H might not have order L, breaking the homomorphic property:
127
+ * (r1 + r2) mod L * H would not equal r1*H + r2*H
128
+ */
129
+ function generateH(): typeof G {
130
+ let counter = 0
131
+
132
+ while (counter < 256) {
133
+ // Create candidate by hashing domain + counter
134
+ const input = new TextEncoder().encode(`${H_DOMAIN}:${counter}`)
135
+ const hashBytes = sha256(input)
136
+
137
+ try {
138
+ // Try to interpret hash as a compressed ed25519 point
139
+ // Ed25519 points are encoded as the y-coordinate with sign bit
140
+ const rawPoint = ed25519.ExtendedPoint.fromHex(hashBytes)
141
+
142
+ // Clear the cofactor: multiply by 8 to get a point in the prime-order subgroup
143
+ // This ensures the point has order L (not 2L, 4L, or 8L)
144
+ const point = rawPoint.multiply(ED25519_COFACTOR)
145
+
146
+ // Ensure point is not identity (could happen if rawPoint had small order)
147
+ if (!point.equals(ed25519.ExtendedPoint.ZERO)) {
148
+ // Check it's not G by comparing serialized form
149
+ const gBytes = G.toRawBytes()
150
+ const hBytes = point.toRawBytes()
151
+ if (bytesToHex(gBytes) !== bytesToHex(hBytes)) {
152
+ return point
153
+ }
154
+ }
155
+ } catch {
156
+ // Not a valid point encoding, try next counter
157
+ }
158
+
159
+ counter++
160
+ }
161
+
162
+ throw new CryptoError(
163
+ 'Failed to generate H point - this should never happen',
164
+ ErrorCode.CRYPTO_FAILED,
165
+ { context: { domain: H_DOMAIN } }
166
+ )
167
+ }
168
+
169
+ // ─── Core Functions ───────────────────────────────────────────────────────────
170
+
171
+ /**
172
+ * Create a Pedersen commitment to a value (ed25519)
173
+ *
174
+ * C = v*G + r*H
175
+ *
176
+ * @param value - The value to commit to (must be < curve order)
177
+ * @param blinding - Optional blinding factor (random 32 bytes if not provided)
178
+ * @returns The commitment and blinding factor
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * // Commit to 1000 tokens (smallest units)
183
+ * const { commitment, blinding } = commitSolana(1000n)
184
+ *
185
+ * // Verify the commitment
186
+ * const valid = verifyOpeningSolana(commitment, 1000n, blinding)
187
+ * ```
188
+ */
189
+ export function commitSolana(
190
+ value: bigint,
191
+ blinding?: Uint8Array,
192
+ ): SolanaPedersenCommitment {
193
+ // Validate value type
194
+ if (typeof value !== 'bigint') {
195
+ throw new ValidationError('must be a bigint', 'value', { received: typeof value })
196
+ }
197
+
198
+ // Validate value is in valid range
199
+ if (value < 0n) {
200
+ throw new ValidationError('must be non-negative', 'value')
201
+ }
202
+ if (value >= ED25519_ORDER) {
203
+ throw new ValidationError(
204
+ 'must be less than curve order',
205
+ 'value',
206
+ { curveOrder: ED25519_ORDER.toString(16) }
207
+ )
208
+ }
209
+
210
+ // Generate or use provided blinding factor
211
+ const r = blinding ?? randomBytes(32)
212
+ if (r.length !== 32) {
213
+ throw new ValidationError('must be 32 bytes', 'blinding', { received: r.length })
214
+ }
215
+
216
+ // Ensure blinding is in valid range (mod L)
217
+ const rScalar = bytesToBigInt(r) % ED25519_ORDER
218
+ if (rScalar === 0n) {
219
+ // Regenerate if we got zero (extremely rare)
220
+ return commitSolana(value, randomBytes(32))
221
+ }
222
+
223
+ // C = v*G + r*H
224
+ let C: typeof G
225
+
226
+ if (value === 0n) {
227
+ // Only blinding contributes: C = r*H
228
+ C = H.multiply(rScalar)
229
+ } else {
230
+ // Normal case: C = v*G + r*H
231
+ const vG = G.multiply(value)
232
+ const rH = H.multiply(rScalar)
233
+ C = vG.add(rH)
234
+ }
235
+
236
+ // Return the reduced scalar as blinding (important for homomorphic ops)
237
+ const rScalarBytes = bigIntToBytes(rScalar, 32)
238
+
239
+ return {
240
+ commitment: `0x${bytesToHex(C.toRawBytes())}` as HexString,
241
+ blinding: `0x${bytesToHex(rScalarBytes)}` as HexString,
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Verify that a commitment opens to a specific value
247
+ *
248
+ * @param commitment - The commitment point to verify
249
+ * @param value - The claimed value
250
+ * @param blinding - The blinding factor used
251
+ * @returns true if the commitment opens correctly
252
+ */
253
+ export function verifyOpeningSolana(
254
+ commitment: HexString,
255
+ value: bigint,
256
+ blinding: HexString,
257
+ ): boolean {
258
+ try {
259
+ // Parse the commitment point
260
+ const commitmentBytes = hexToBytes(commitment.slice(2))
261
+ const C = ed25519.ExtendedPoint.fromHex(commitmentBytes)
262
+
263
+ // Parse blinding factor
264
+ const blindingBytes = hexToBytes(blinding.slice(2))
265
+ const rScalar = bytesToBigInt(blindingBytes) % ED25519_ORDER
266
+
267
+ // Recompute expected commitment
268
+ let expected: typeof G
269
+
270
+ if (value === 0n) {
271
+ expected = H.multiply(rScalar)
272
+ } else if (rScalar === 0n) {
273
+ expected = G.multiply(value)
274
+ } else {
275
+ const vG = G.multiply(value)
276
+ const rH = H.multiply(rScalar)
277
+ expected = vG.add(rH)
278
+ }
279
+
280
+ // Check equality
281
+ return C.equals(expected)
282
+ } catch {
283
+ return false
284
+ }
285
+ }
286
+
287
+ // ─── SPL Token Specific Functions ─────────────────────────────────────────────
288
+
289
+ /**
290
+ * Create a commitment for an SPL token amount
291
+ *
292
+ * Handles SPL token decimals and u64 amount validation.
293
+ *
294
+ * @param amount - Token amount in smallest units (u64)
295
+ * @param mint - SPL token mint address
296
+ * @param decimals - Token decimals (e.g., 6 for USDC, 9 for SOL)
297
+ * @returns SPL token commitment with metadata
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * // Commit to 100 USDC (6 decimals)
302
+ * const commitment = commitSPLToken(
303
+ * 100_000_000n, // 100 USDC in smallest units
304
+ * 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC mint
305
+ * 6
306
+ * )
307
+ * ```
308
+ */
309
+ export function commitSPLToken(
310
+ amount: bigint,
311
+ mint: string,
312
+ decimals: number,
313
+ blinding?: Uint8Array,
314
+ ): SPLTokenCommitment {
315
+ // Validate amount is valid u64
316
+ if (amount < 0n || amount > MAX_SPL_AMOUNT) {
317
+ throw new ValidationError(
318
+ 'SPL token amount must be a valid u64 (0 to 2^64-1)',
319
+ 'amount',
320
+ { max: MAX_SPL_AMOUNT.toString() }
321
+ )
322
+ }
323
+
324
+ // Validate decimals
325
+ if (decimals < 0 || decimals > 18 || !Number.isInteger(decimals)) {
326
+ throw new ValidationError(
327
+ 'decimals must be an integer between 0 and 18',
328
+ 'decimals'
329
+ )
330
+ }
331
+
332
+ // Create the commitment
333
+ const { commitment, blinding: blindingHex } = commitSolana(amount, blinding)
334
+
335
+ return {
336
+ commitment,
337
+ blinding: blindingHex,
338
+ mint,
339
+ decimals,
340
+ amountRaw: amount,
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Verify an SPL token commitment
346
+ *
347
+ * @param tokenCommitment - The SPL token commitment to verify
348
+ * @param expectedAmount - The expected amount in smallest units
349
+ * @returns true if the commitment opens correctly
350
+ */
351
+ export function verifySPLTokenCommitment(
352
+ tokenCommitment: SPLTokenCommitment,
353
+ expectedAmount: bigint,
354
+ ): boolean {
355
+ return verifyOpeningSolana(
356
+ tokenCommitment.commitment,
357
+ expectedAmount,
358
+ tokenCommitment.blinding
359
+ )
360
+ }
361
+
362
+ /**
363
+ * Convert human-readable token amount to smallest units
364
+ *
365
+ * @param amount - Human-readable amount (e.g., 100.5)
366
+ * @param decimals - Token decimals
367
+ * @returns Amount in smallest units as bigint
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * // 100.5 USDC (6 decimals) = 100,500,000 units
372
+ * const units = toSmallestUnits(100.5, 6)
373
+ * // 100_500_000n
374
+ * ```
375
+ */
376
+ export function toSmallestUnits(amount: number | string, decimals: number): bigint {
377
+ const [whole, fraction = ''] = String(amount).split('.')
378
+ const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals)
379
+ return BigInt(whole + paddedFraction)
380
+ }
381
+
382
+ /**
383
+ * Convert smallest units to human-readable amount
384
+ *
385
+ * @param amount - Amount in smallest units
386
+ * @param decimals - Token decimals
387
+ * @returns Human-readable string
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * // 100,500,000 units (6 decimals) = "100.5"
392
+ * const readable = fromSmallestUnits(100_500_000n, 6)
393
+ * // "100.5"
394
+ * ```
395
+ */
396
+ export function fromSmallestUnits(amount: bigint, decimals: number): string {
397
+ const str = amount.toString().padStart(decimals + 1, '0')
398
+ const whole = str.slice(0, -decimals) || '0'
399
+ const fraction = str.slice(-decimals).replace(/0+$/, '')
400
+ return fraction ? `${whole}.${fraction}` : whole
401
+ }
402
+
403
+ // ─── Homomorphic Operations ───────────────────────────────────────────────────
404
+
405
+ /**
406
+ * Add two commitments homomorphically (ed25519)
407
+ *
408
+ * C1 + C2 = (v1+v2)*G + (r1+r2)*H
409
+ *
410
+ * @param c1 - First commitment point
411
+ * @param c2 - Second commitment point
412
+ * @returns Sum of commitments
413
+ */
414
+ export function addCommitmentsSolana(
415
+ c1: HexString,
416
+ c2: HexString,
417
+ ): SolanaCommitmentPoint {
418
+ if (!isValidHex(c1)) {
419
+ throw new ValidationError('must be a valid hex string', 'c1')
420
+ }
421
+ if (!isValidHex(c2)) {
422
+ throw new ValidationError('must be a valid hex string', 'c2')
423
+ }
424
+
425
+ let point1: typeof G
426
+ let point2: typeof G
427
+
428
+ try {
429
+ point1 = ed25519.ExtendedPoint.fromHex(hexToBytes(c1.slice(2)))
430
+ } catch {
431
+ throw new ValidationError('must be a valid ed25519 point', 'c1')
432
+ }
433
+ try {
434
+ point2 = ed25519.ExtendedPoint.fromHex(hexToBytes(c2.slice(2)))
435
+ } catch {
436
+ throw new ValidationError('must be a valid ed25519 point', 'c2')
437
+ }
438
+
439
+ const sum = point1.add(point2)
440
+
441
+ return {
442
+ commitment: `0x${bytesToHex(sum.toRawBytes())}` as HexString,
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Subtract two commitments homomorphically (ed25519)
448
+ *
449
+ * C1 - C2 = (v1-v2)*G + (r1-r2)*H
450
+ *
451
+ * @param c1 - First commitment point
452
+ * @param c2 - Second commitment point (to subtract)
453
+ * @returns Difference of commitments
454
+ */
455
+ export function subtractCommitmentsSolana(
456
+ c1: HexString,
457
+ c2: HexString,
458
+ ): SolanaCommitmentPoint {
459
+ if (!isValidHex(c1)) {
460
+ throw new ValidationError('must be a valid hex string', 'c1')
461
+ }
462
+ if (!isValidHex(c2)) {
463
+ throw new ValidationError('must be a valid hex string', 'c2')
464
+ }
465
+
466
+ let point1: typeof G
467
+ let point2: typeof G
468
+
469
+ try {
470
+ point1 = ed25519.ExtendedPoint.fromHex(hexToBytes(c1.slice(2)))
471
+ } catch {
472
+ throw new ValidationError('must be a valid ed25519 point', 'c1')
473
+ }
474
+ try {
475
+ point2 = ed25519.ExtendedPoint.fromHex(hexToBytes(c2.slice(2)))
476
+ } catch {
477
+ throw new ValidationError('must be a valid ed25519 point', 'c2')
478
+ }
479
+
480
+ const diff = point1.subtract(point2)
481
+
482
+ // Handle identity point
483
+ if (diff.equals(ed25519.ExtendedPoint.ZERO)) {
484
+ // Return zero point representation
485
+ return {
486
+ commitment: '0x' + '00'.repeat(32) as HexString,
487
+ }
488
+ }
489
+
490
+ return {
491
+ commitment: `0x${bytesToHex(diff.toRawBytes())}` as HexString,
492
+ }
493
+ }
494
+
495
+ /**
496
+ * Add blinding factors (ed25519)
497
+ *
498
+ * @param b1 - First blinding factor
499
+ * @param b2 - Second blinding factor
500
+ * @returns Sum of blindings (mod curve order)
501
+ */
502
+ export function addBlindingsSolana(b1: HexString, b2: HexString): HexString {
503
+ const r1 = bytesToBigInt(hexToBytes(b1.slice(2)))
504
+ const r2 = bytesToBigInt(hexToBytes(b2.slice(2)))
505
+
506
+ const sum = (r1 + r2) % ED25519_ORDER
507
+ const sumBytes = bigIntToBytes(sum, 32)
508
+
509
+ return `0x${bytesToHex(sumBytes)}` as HexString
510
+ }
511
+
512
+ /**
513
+ * Subtract blinding factors (ed25519)
514
+ *
515
+ * @param b1 - First blinding factor
516
+ * @param b2 - Second blinding factor (to subtract)
517
+ * @returns Difference of blindings (mod curve order)
518
+ */
519
+ export function subtractBlindingsSolana(b1: HexString, b2: HexString): HexString {
520
+ const r1 = bytesToBigInt(hexToBytes(b1.slice(2)))
521
+ const r2 = bytesToBigInt(hexToBytes(b2.slice(2)))
522
+
523
+ const diff = (r1 - r2 + ED25519_ORDER) % ED25519_ORDER
524
+ const diffBytes = bigIntToBytes(diff, 32)
525
+
526
+ return `0x${bytesToHex(diffBytes)}` as HexString
527
+ }
528
+
529
+ // ─── Generator Access ─────────────────────────────────────────────────────────
530
+
531
+ /**
532
+ * Get the ed25519 generators for ZK proof integration
533
+ */
534
+ export function getGeneratorsSolana(): {
535
+ G: HexString
536
+ H: HexString
537
+ } {
538
+ return {
539
+ G: `0x${bytesToHex(G.toRawBytes())}` as HexString,
540
+ H: `0x${bytesToHex(H.toRawBytes())}` as HexString,
541
+ }
542
+ }
543
+
544
+ /**
545
+ * Generate a random blinding factor (32 bytes)
546
+ */
547
+ export function generateBlindingSolana(): HexString {
548
+ return `0x${bytesToHex(randomBytes(32))}` as HexString
549
+ }
550
+
551
+ // ─── Utility Functions ────────────────────────────────────────────────────────
552
+
553
+ function bytesToBigInt(bytes: Uint8Array): bigint {
554
+ let result = 0n
555
+ for (const byte of bytes) {
556
+ result = (result << 8n) + BigInt(byte)
557
+ }
558
+ return result
559
+ }
560
+
561
+ function bigIntToBytes(value: bigint, length: number): Uint8Array {
562
+ const bytes = new Uint8Array(length)
563
+ let v = value
564
+ for (let i = length - 1; i >= 0; i--) {
565
+ bytes[i] = Number(v & 0xffn)
566
+ v >>= 8n
567
+ }
568
+ return bytes
569
+ }
570
+
571
+ function hexToBytes(hex: string): Uint8Array {
572
+ const bytes = new Uint8Array(hex.length / 2)
573
+ for (let i = 0; i < hex.length; i += 2) {
574
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16)
575
+ }
576
+ return bytes
577
+ }