@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,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Anchor-based Shielded Transfer
|
|
3
|
+
*
|
|
4
|
+
* Uses the SIP Privacy Anchor program for on-chain privacy with:
|
|
5
|
+
* - Pedersen commitments for hidden amounts
|
|
6
|
+
* - Stealth addresses for hidden recipients
|
|
7
|
+
* - Viewing keys for compliance
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
PublicKey,
|
|
12
|
+
Transaction,
|
|
13
|
+
TransactionInstruction,
|
|
14
|
+
SystemProgram,
|
|
15
|
+
Connection,
|
|
16
|
+
} from '@solana/web3.js'
|
|
17
|
+
import { sha256 } from '@noble/hashes/sha2'
|
|
18
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
|
19
|
+
import { ValidationError } from '../../errors'
|
|
20
|
+
import { commit } from '../../commitment'
|
|
21
|
+
import {
|
|
22
|
+
generateEd25519StealthAddress,
|
|
23
|
+
ed25519PublicKeyToSolanaAddress,
|
|
24
|
+
decodeStealthMetaAddress,
|
|
25
|
+
} from '../../stealth'
|
|
26
|
+
import type { StealthMetaAddress, HexString, StealthAddress } from '@sip-protocol/types'
|
|
27
|
+
|
|
28
|
+
// Program ID - deployed on devnet
|
|
29
|
+
export const SIP_PRIVACY_PROGRAM_ID = new PublicKey(
|
|
30
|
+
'S1PMFspo4W6BYKHWkHNF7kZ3fnqibEXg3LQjxepS9at'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
// Config PDA - initialized on devnet
|
|
34
|
+
export const CONFIG_PDA = new PublicKey(
|
|
35
|
+
'BVawZkppFewygA5nxdrLma4ThKx8Th7bW4KTCkcWTZwZ'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// Treasury/fee collector (using authority for now)
|
|
39
|
+
export const FEE_COLLECTOR = new PublicKey(
|
|
40
|
+
'S1P6j1yeTm6zkewQVeihrTZvmfoHABRkHDhabWTuWMd'
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Seeds
|
|
44
|
+
const TRANSFER_RECORD_SEED = Buffer.from('transfer_record')
|
|
45
|
+
|
|
46
|
+
// Instruction discriminators (first 8 bytes of sha256("global:<instruction_name>"))
|
|
47
|
+
const SHIELDED_TRANSFER_DISCRIMINATOR = Buffer.from([
|
|
48
|
+
0x9d, 0x2a, 0x42, 0x93, 0xee, 0x75, 0x61, 0x5c
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
export interface AnchorShieldedTransferParams {
|
|
52
|
+
/** Solana connection */
|
|
53
|
+
connection: Connection
|
|
54
|
+
/** Sender's public key */
|
|
55
|
+
sender: PublicKey
|
|
56
|
+
/** Recipient's SIP address (sip:solana:spending:viewing) or StealthMetaAddress */
|
|
57
|
+
recipient: string | StealthMetaAddress
|
|
58
|
+
/** Amount in lamports */
|
|
59
|
+
amount: bigint
|
|
60
|
+
/** Transaction signer */
|
|
61
|
+
signTransaction: <T extends Transaction>(tx: T) => Promise<T>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface AnchorShieldedTransferResult {
|
|
65
|
+
/** Transaction signature */
|
|
66
|
+
signature: string
|
|
67
|
+
/** Transfer record PDA (noteId) */
|
|
68
|
+
noteId: string
|
|
69
|
+
/** Stealth address funds were sent to */
|
|
70
|
+
stealthAddress: string
|
|
71
|
+
/** Ephemeral public key for recipient scanning */
|
|
72
|
+
ephemeralPublicKey: HexString
|
|
73
|
+
/** Amount commitment */
|
|
74
|
+
commitment: HexString
|
|
75
|
+
/** View tag for quick filtering */
|
|
76
|
+
viewTag: string
|
|
77
|
+
/** Viewing key hash for compliance */
|
|
78
|
+
viewingKeyHash: HexString
|
|
79
|
+
/** Explorer URL */
|
|
80
|
+
explorerUrl: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Execute a shielded SOL transfer using the Anchor program
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const result = await shieldedTransfer({
|
|
89
|
+
* connection,
|
|
90
|
+
* sender: wallet.publicKey,
|
|
91
|
+
* recipient: 'sip:solana:0x02abc...:0x03def...',
|
|
92
|
+
* amount: 1_000_000_000n, // 1 SOL
|
|
93
|
+
* signTransaction: wallet.signTransaction,
|
|
94
|
+
* })
|
|
95
|
+
*
|
|
96
|
+
* console.log('Tx:', result.signature)
|
|
97
|
+
* console.log('Note ID:', result.noteId)
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export async function shieldedTransfer(
|
|
101
|
+
params: AnchorShieldedTransferParams
|
|
102
|
+
): Promise<AnchorShieldedTransferResult> {
|
|
103
|
+
const { connection, sender, recipient, amount, signTransaction } = params
|
|
104
|
+
|
|
105
|
+
// Validate inputs
|
|
106
|
+
if (!connection) {
|
|
107
|
+
throw new ValidationError('connection is required', 'connection')
|
|
108
|
+
}
|
|
109
|
+
if (!sender) {
|
|
110
|
+
throw new ValidationError('sender is required', 'sender')
|
|
111
|
+
}
|
|
112
|
+
if (!recipient) {
|
|
113
|
+
throw new ValidationError('recipient is required', 'recipient')
|
|
114
|
+
}
|
|
115
|
+
if (amount <= 0n) {
|
|
116
|
+
throw new ValidationError('amount must be positive', 'amount')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Parse recipient address
|
|
120
|
+
const recipientMeta: StealthMetaAddress = typeof recipient === 'string'
|
|
121
|
+
? decodeStealthMetaAddress(recipient)
|
|
122
|
+
: recipient
|
|
123
|
+
|
|
124
|
+
if (recipientMeta.chain !== 'solana') {
|
|
125
|
+
throw new ValidationError(
|
|
126
|
+
`Expected solana chain, got ${recipientMeta.chain}`,
|
|
127
|
+
'recipient'
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 1. Generate stealth address
|
|
132
|
+
const stealthResult = generateEd25519StealthAddress(recipientMeta)
|
|
133
|
+
const stealthAddr: StealthAddress = stealthResult.stealthAddress
|
|
134
|
+
const stealthPubkey = ed25519PublicKeyToSolanaAddress(stealthAddr.address)
|
|
135
|
+
const stealthAccountPubkey = new PublicKey(stealthPubkey)
|
|
136
|
+
|
|
137
|
+
// 2. Generate Pedersen commitment
|
|
138
|
+
const { commitment, blinding } = commit(amount)
|
|
139
|
+
const commitmentBytes = hexToBytes(commitment.slice(2))
|
|
140
|
+
|
|
141
|
+
// Ensure commitment is 33 bytes (compressed point)
|
|
142
|
+
if (commitmentBytes.length !== 33) {
|
|
143
|
+
throw new ValidationError(
|
|
144
|
+
`Invalid commitment size: ${commitmentBytes.length}, expected 33`,
|
|
145
|
+
'commitment'
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 3. Prepare ephemeral public key (33 bytes compressed)
|
|
150
|
+
const ephemeralPubkeyHex = stealthAddr.ephemeralPublicKey
|
|
151
|
+
const ephemeralPubkeyBytes = hexToBytes(ephemeralPubkeyHex.slice(2))
|
|
152
|
+
|
|
153
|
+
// Pad or truncate to 33 bytes if needed
|
|
154
|
+
const ephemeralPubkey33 = new Uint8Array(33)
|
|
155
|
+
ephemeralPubkey33[0] = 0x02 // compressed prefix
|
|
156
|
+
ephemeralPubkey33.set(ephemeralPubkeyBytes.slice(0, 32), 1)
|
|
157
|
+
|
|
158
|
+
// 4. Compute viewing key hash
|
|
159
|
+
const viewingKeyBytes = hexToBytes(recipientMeta.viewingKey.slice(2))
|
|
160
|
+
const viewingKeyHash = sha256(viewingKeyBytes)
|
|
161
|
+
|
|
162
|
+
// 5. Encrypt amount with viewing key
|
|
163
|
+
const encryptedAmount = encryptAmount(amount, recipientMeta.viewingKey)
|
|
164
|
+
const encryptedAmountBytes = hexToBytes(encryptedAmount.slice(2))
|
|
165
|
+
|
|
166
|
+
// 6. Create mock proof (real ZK proof integration in future)
|
|
167
|
+
const mockProof = createMockProof(commitment, amount, blinding)
|
|
168
|
+
|
|
169
|
+
// 7. Get current transfer count to derive PDA
|
|
170
|
+
const configAccount = await connection.getAccountInfo(CONFIG_PDA)
|
|
171
|
+
if (!configAccount) {
|
|
172
|
+
throw new ValidationError('SIP Privacy program not initialized', 'config')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Parse total_transfers from config (offset: 8 + 32 + 2 + 1 = 43, size: 8)
|
|
176
|
+
const totalTransfers = configAccount.data.readBigUInt64LE(43)
|
|
177
|
+
|
|
178
|
+
// 8. Derive transfer record PDA
|
|
179
|
+
const [transferRecordPda] = PublicKey.findProgramAddressSync(
|
|
180
|
+
[
|
|
181
|
+
TRANSFER_RECORD_SEED,
|
|
182
|
+
sender.toBuffer(),
|
|
183
|
+
Buffer.from(bigintToLeBytes(totalTransfers)),
|
|
184
|
+
],
|
|
185
|
+
SIP_PRIVACY_PROGRAM_ID
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
// 9. Build instruction data
|
|
189
|
+
const instructionData = buildShieldedTransferData({
|
|
190
|
+
commitment: commitmentBytes,
|
|
191
|
+
stealthPubkey: stealthAccountPubkey,
|
|
192
|
+
ephemeralPubkey: ephemeralPubkey33,
|
|
193
|
+
viewingKeyHash,
|
|
194
|
+
encryptedAmount: encryptedAmountBytes,
|
|
195
|
+
proof: mockProof,
|
|
196
|
+
actualAmount: amount,
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// 10. Create instruction
|
|
200
|
+
const instruction = new TransactionInstruction({
|
|
201
|
+
keys: [
|
|
202
|
+
{ pubkey: CONFIG_PDA, isSigner: false, isWritable: true },
|
|
203
|
+
{ pubkey: transferRecordPda, isSigner: false, isWritable: true },
|
|
204
|
+
{ pubkey: sender, isSigner: true, isWritable: true },
|
|
205
|
+
{ pubkey: stealthAccountPubkey, isSigner: false, isWritable: true },
|
|
206
|
+
{ pubkey: FEE_COLLECTOR, isSigner: false, isWritable: true },
|
|
207
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
208
|
+
],
|
|
209
|
+
programId: SIP_PRIVACY_PROGRAM_ID,
|
|
210
|
+
data: instructionData,
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
// 11. Build transaction
|
|
214
|
+
const transaction = new Transaction()
|
|
215
|
+
transaction.add(instruction)
|
|
216
|
+
|
|
217
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
|
218
|
+
transaction.recentBlockhash = blockhash
|
|
219
|
+
transaction.feePayer = sender
|
|
220
|
+
|
|
221
|
+
// 12. Sign and send
|
|
222
|
+
const signedTx = await signTransaction(transaction)
|
|
223
|
+
const signature = await connection.sendRawTransaction(signedTx.serialize())
|
|
224
|
+
|
|
225
|
+
// Wait for confirmation
|
|
226
|
+
await connection.confirmTransaction({
|
|
227
|
+
signature,
|
|
228
|
+
blockhash,
|
|
229
|
+
lastValidBlockHeight,
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// 13. Return result
|
|
233
|
+
const cluster = await getCluster(connection)
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
signature,
|
|
237
|
+
noteId: transferRecordPda.toBase58(),
|
|
238
|
+
stealthAddress: stealthAccountPubkey.toBase58(),
|
|
239
|
+
ephemeralPublicKey: ephemeralPubkeyHex,
|
|
240
|
+
commitment,
|
|
241
|
+
viewTag: `0x${stealthAddr.viewTag.toString(16).padStart(2, '0')}`,
|
|
242
|
+
viewingKeyHash: `0x${bytesToHex(viewingKeyHash)}`,
|
|
243
|
+
explorerUrl: `https://solscan.io/tx/${signature}?cluster=${cluster}`,
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Build the instruction data for shielded_transfer
|
|
249
|
+
*/
|
|
250
|
+
function buildShieldedTransferData(params: {
|
|
251
|
+
commitment: Uint8Array
|
|
252
|
+
stealthPubkey: PublicKey
|
|
253
|
+
ephemeralPubkey: Uint8Array
|
|
254
|
+
viewingKeyHash: Uint8Array
|
|
255
|
+
encryptedAmount: Uint8Array
|
|
256
|
+
proof: Uint8Array
|
|
257
|
+
actualAmount: bigint
|
|
258
|
+
}): Buffer {
|
|
259
|
+
const {
|
|
260
|
+
commitment,
|
|
261
|
+
stealthPubkey,
|
|
262
|
+
ephemeralPubkey,
|
|
263
|
+
viewingKeyHash,
|
|
264
|
+
encryptedAmount,
|
|
265
|
+
proof,
|
|
266
|
+
actualAmount,
|
|
267
|
+
} = params
|
|
268
|
+
|
|
269
|
+
// Calculate total size
|
|
270
|
+
const size =
|
|
271
|
+
8 + // discriminator
|
|
272
|
+
33 + // commitment
|
|
273
|
+
32 + // stealth_pubkey
|
|
274
|
+
33 + // ephemeral_pubkey
|
|
275
|
+
32 + // viewing_key_hash
|
|
276
|
+
4 + encryptedAmount.length + // encrypted_amount (Vec<u8>)
|
|
277
|
+
4 + proof.length + // proof (Vec<u8>)
|
|
278
|
+
8 // actual_amount
|
|
279
|
+
|
|
280
|
+
const buffer = Buffer.alloc(size)
|
|
281
|
+
let offset = 0
|
|
282
|
+
|
|
283
|
+
// Discriminator
|
|
284
|
+
SHIELDED_TRANSFER_DISCRIMINATOR.copy(buffer, offset)
|
|
285
|
+
offset += 8
|
|
286
|
+
|
|
287
|
+
// Commitment [u8; 33]
|
|
288
|
+
buffer.set(commitment, offset)
|
|
289
|
+
offset += 33
|
|
290
|
+
|
|
291
|
+
// Stealth pubkey (Pubkey = 32 bytes)
|
|
292
|
+
buffer.set(stealthPubkey.toBuffer(), offset)
|
|
293
|
+
offset += 32
|
|
294
|
+
|
|
295
|
+
// Ephemeral pubkey [u8; 33]
|
|
296
|
+
buffer.set(ephemeralPubkey, offset)
|
|
297
|
+
offset += 33
|
|
298
|
+
|
|
299
|
+
// Viewing key hash [u8; 32]
|
|
300
|
+
buffer.set(viewingKeyHash, offset)
|
|
301
|
+
offset += 32
|
|
302
|
+
|
|
303
|
+
// Encrypted amount Vec<u8> (4-byte length prefix + data)
|
|
304
|
+
buffer.writeUInt32LE(encryptedAmount.length, offset)
|
|
305
|
+
offset += 4
|
|
306
|
+
buffer.set(encryptedAmount, offset)
|
|
307
|
+
offset += encryptedAmount.length
|
|
308
|
+
|
|
309
|
+
// Proof Vec<u8>
|
|
310
|
+
buffer.writeUInt32LE(proof.length, offset)
|
|
311
|
+
offset += 4
|
|
312
|
+
buffer.set(proof, offset)
|
|
313
|
+
offset += proof.length
|
|
314
|
+
|
|
315
|
+
// Actual amount u64
|
|
316
|
+
buffer.writeBigUInt64LE(actualAmount, offset)
|
|
317
|
+
|
|
318
|
+
return buffer
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Create a mock proof for development (real ZK proof in future)
|
|
323
|
+
*/
|
|
324
|
+
function createMockProof(
|
|
325
|
+
commitment: HexString,
|
|
326
|
+
amount: bigint,
|
|
327
|
+
blinding: HexString
|
|
328
|
+
): Uint8Array {
|
|
329
|
+
// Mock proof: 128 bytes of deterministic data
|
|
330
|
+
const proof = new Uint8Array(128)
|
|
331
|
+
const commitmentBytes = hexToBytes(commitment.slice(2))
|
|
332
|
+
|
|
333
|
+
// Fill with hash of inputs for reproducibility
|
|
334
|
+
const inputHash = sha256(
|
|
335
|
+
new Uint8Array([
|
|
336
|
+
...commitmentBytes,
|
|
337
|
+
...bigintToLeBytes(amount),
|
|
338
|
+
...hexToBytes(blinding.slice(2)),
|
|
339
|
+
])
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
proof.set(inputHash, 0)
|
|
343
|
+
proof.set(inputHash, 32)
|
|
344
|
+
proof.set(inputHash, 64)
|
|
345
|
+
proof.set(inputHash, 96)
|
|
346
|
+
|
|
347
|
+
return proof
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Convert bigint to little-endian bytes (8 bytes)
|
|
352
|
+
*/
|
|
353
|
+
function bigintToLeBytes(value: bigint): Uint8Array {
|
|
354
|
+
const buffer = new ArrayBuffer(8)
|
|
355
|
+
const view = new DataView(buffer)
|
|
356
|
+
view.setBigUint64(0, value, true)
|
|
357
|
+
return new Uint8Array(buffer)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Detect cluster from connection
|
|
362
|
+
*/
|
|
363
|
+
async function getCluster(connection: Connection): Promise<string> {
|
|
364
|
+
const endpoint = connection.rpcEndpoint
|
|
365
|
+
if (endpoint.includes('devnet')) return 'devnet'
|
|
366
|
+
if (endpoint.includes('testnet')) return 'testnet'
|
|
367
|
+
if (endpoint.includes('mainnet') || endpoint.includes('api.mainnet-beta')) return 'mainnet'
|
|
368
|
+
return 'devnet'
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Encrypt amount using viewing key (XChaCha20-Poly1305)
|
|
373
|
+
*/
|
|
374
|
+
function encryptAmount(amount: bigint, viewingKey: HexString): HexString {
|
|
375
|
+
// Simple encryption: amount XOR'd with viewing key hash
|
|
376
|
+
// TODO: Use proper XChaCha20-Poly1305 from @noble/ciphers
|
|
377
|
+
const amountBytes = bigintToLeBytes(amount)
|
|
378
|
+
const keyHash = sha256(hexToBytes(viewingKey.slice(2)))
|
|
379
|
+
|
|
380
|
+
const encrypted = new Uint8Array(amountBytes.length)
|
|
381
|
+
for (let i = 0; i < amountBytes.length; i++) {
|
|
382
|
+
encrypted[i] = amountBytes[i] ^ keyHash[i]
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return `0x${bytesToHex(encrypted)}`
|
|
386
|
+
}
|