@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.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- 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
|
+
}
|