@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,907 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C-SPL (Confidential SPL) Token Client
|
|
3
|
+
*
|
|
4
|
+
* Client for interacting with Confidential SPL tokens on Solana.
|
|
5
|
+
* Provides wrapping, unwrapping, transfers, and balance queries for C-SPL tokens.
|
|
6
|
+
*
|
|
7
|
+
* ## Features
|
|
8
|
+
*
|
|
9
|
+
* - Wrap any SPL token to its confidential version
|
|
10
|
+
* - Transfer C-SPL tokens with encrypted amounts
|
|
11
|
+
* - Query confidential balances
|
|
12
|
+
* - Encrypt/decrypt amounts for transfers
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { CSPLClient, CSPL_TOKENS } from '@sip-protocol/sdk'
|
|
18
|
+
*
|
|
19
|
+
* const client = new CSPLClient({ rpcUrl: 'https://api.devnet.solana.com' })
|
|
20
|
+
*
|
|
21
|
+
* // Wrap 1 SOL to C-wSOL
|
|
22
|
+
* const wrapResult = await client.wrapToken({
|
|
23
|
+
* mint: CSPL_TOKENS['C-wSOL'].mint!,
|
|
24
|
+
* amount: 1_000_000_000n, // 1 SOL
|
|
25
|
+
* owner: walletAddress,
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* // Transfer confidentially
|
|
29
|
+
* const encryptedAmount = await client.encryptAmount({
|
|
30
|
+
* amount: 500_000_000n,
|
|
31
|
+
* recipientPubkey: recipientAddress,
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* await client.transfer({
|
|
35
|
+
* from: walletAddress,
|
|
36
|
+
* to: recipientAddress,
|
|
37
|
+
* token: wrapResult.token!,
|
|
38
|
+
* encryptedAmount: encryptedAmount.ciphertext,
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see https://docs.arcium.com
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import type {
|
|
46
|
+
CSPLToken,
|
|
47
|
+
ConfidentialTokenAccount,
|
|
48
|
+
ConfidentialBalance,
|
|
49
|
+
ConfidentialTransferParams,
|
|
50
|
+
ConfidentialTransferResult,
|
|
51
|
+
WrapTokenParams,
|
|
52
|
+
WrapTokenResult,
|
|
53
|
+
UnwrapTokenParams,
|
|
54
|
+
UnwrapTokenResult,
|
|
55
|
+
CSPLEncryptionParams,
|
|
56
|
+
CSPLDecryptionParams,
|
|
57
|
+
EncryptedAmount,
|
|
58
|
+
CSPLEncryptionType,
|
|
59
|
+
ICSPLClient,
|
|
60
|
+
} from './cspl-types'
|
|
61
|
+
|
|
62
|
+
import {
|
|
63
|
+
CSPL_TOKENS,
|
|
64
|
+
CSPL_PROGRAM_IDS,
|
|
65
|
+
CSPL_OPERATION_COSTS,
|
|
66
|
+
CSPL_OPERATION_TIMES,
|
|
67
|
+
CSPL_MAX_MEMO_BYTES,
|
|
68
|
+
} from './cspl-types'
|
|
69
|
+
|
|
70
|
+
import { isValidSolanaAddressFormat } from '../validation'
|
|
71
|
+
import { deepFreeze } from './interface'
|
|
72
|
+
import {
|
|
73
|
+
LRUCache,
|
|
74
|
+
DEFAULT_CACHE_SIZES,
|
|
75
|
+
DEFAULT_CACHE_TTL,
|
|
76
|
+
type LRUCacheStats,
|
|
77
|
+
} from './lru-cache'
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Cache configuration for CSPLClient
|
|
81
|
+
*/
|
|
82
|
+
export interface CSPLCacheConfig {
|
|
83
|
+
/** Maximum entries in account cache (default: 1000) */
|
|
84
|
+
accountCacheSize?: number
|
|
85
|
+
/** Maximum entries in balance cache (default: 500) */
|
|
86
|
+
balanceCacheSize?: number
|
|
87
|
+
/** Account cache TTL in ms (default: 5 minutes) */
|
|
88
|
+
accountCacheTTL?: number
|
|
89
|
+
/** Balance cache TTL in ms (default: 30 seconds) */
|
|
90
|
+
balanceCacheTTL?: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for CSPLClient
|
|
95
|
+
*/
|
|
96
|
+
export interface CSPLClientConfig {
|
|
97
|
+
/** Solana RPC endpoint URL */
|
|
98
|
+
rpcUrl?: string
|
|
99
|
+
/** Default encryption type */
|
|
100
|
+
defaultEncryption?: CSPLEncryptionType
|
|
101
|
+
/** Enable compliance/audit features */
|
|
102
|
+
enableCompliance?: boolean
|
|
103
|
+
/** Request timeout in milliseconds */
|
|
104
|
+
timeout?: number
|
|
105
|
+
/** Cache configuration */
|
|
106
|
+
cache?: CSPLCacheConfig
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* C-SPL Token Client
|
|
111
|
+
*
|
|
112
|
+
* Handles all operations for Confidential SPL tokens.
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Extended cache stats including LRU metrics
|
|
116
|
+
*/
|
|
117
|
+
export interface CSPLCacheStats {
|
|
118
|
+
/** Account cache statistics */
|
|
119
|
+
accounts: LRUCacheStats
|
|
120
|
+
/** Balance cache statistics */
|
|
121
|
+
balances: LRUCacheStats
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export class CSPLClient implements ICSPLClient {
|
|
125
|
+
private config: Required<Omit<CSPLClientConfig, 'cache'>>
|
|
126
|
+
private cacheConfig: Required<CSPLCacheConfig>
|
|
127
|
+
private connected: boolean = false
|
|
128
|
+
private accountCache: LRUCache<string, ConfidentialTokenAccount>
|
|
129
|
+
private balanceCache: LRUCache<string, ConfidentialBalance>
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create a new C-SPL client
|
|
133
|
+
*
|
|
134
|
+
* @param config - Client configuration
|
|
135
|
+
*/
|
|
136
|
+
constructor(config: CSPLClientConfig = {}) {
|
|
137
|
+
this.config = {
|
|
138
|
+
rpcUrl: config.rpcUrl ?? 'https://api.devnet.solana.com',
|
|
139
|
+
defaultEncryption: config.defaultEncryption ?? 'twisted-elgamal',
|
|
140
|
+
enableCompliance: config.enableCompliance ?? false,
|
|
141
|
+
timeout: config.timeout ?? 30_000,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Initialize cache configuration with defaults
|
|
145
|
+
this.cacheConfig = {
|
|
146
|
+
accountCacheSize: config.cache?.accountCacheSize ?? DEFAULT_CACHE_SIZES.TOKEN_ACCOUNTS,
|
|
147
|
+
balanceCacheSize: config.cache?.balanceCacheSize ?? DEFAULT_CACHE_SIZES.BALANCES,
|
|
148
|
+
accountCacheTTL: config.cache?.accountCacheTTL ?? DEFAULT_CACHE_TTL.TOKEN_ACCOUNTS,
|
|
149
|
+
balanceCacheTTL: config.cache?.balanceCacheTTL ?? DEFAULT_CACHE_TTL.BALANCES,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Initialize LRU caches
|
|
153
|
+
this.accountCache = new LRUCache<string, ConfidentialTokenAccount>({
|
|
154
|
+
maxSize: this.cacheConfig.accountCacheSize,
|
|
155
|
+
ttl: this.cacheConfig.accountCacheTTL,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
this.balanceCache = new LRUCache<string, ConfidentialBalance>({
|
|
159
|
+
maxSize: this.cacheConfig.balanceCacheSize,
|
|
160
|
+
ttl: this.cacheConfig.balanceCacheTTL,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Initialize connection to Solana
|
|
166
|
+
*/
|
|
167
|
+
async connect(rpcUrl?: string): Promise<void> {
|
|
168
|
+
if (rpcUrl) {
|
|
169
|
+
this.config.rpcUrl = rpcUrl
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// In production: Initialize Solana connection
|
|
173
|
+
// const connection = new Connection(this.config.rpcUrl)
|
|
174
|
+
// await connection.getVersion()
|
|
175
|
+
|
|
176
|
+
this.connected = true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Disconnect from Solana
|
|
181
|
+
*/
|
|
182
|
+
async disconnect(): Promise<void> {
|
|
183
|
+
this.connected = false
|
|
184
|
+
this.accountCache.clear()
|
|
185
|
+
this.balanceCache.clear()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Check if client is connected
|
|
190
|
+
*/
|
|
191
|
+
isConnected(): boolean {
|
|
192
|
+
return this.connected
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get or create a confidential token account
|
|
197
|
+
*
|
|
198
|
+
* Creates the account automatically if it doesn't exist.
|
|
199
|
+
*
|
|
200
|
+
* @param owner - Account owner address
|
|
201
|
+
* @param token - C-SPL token configuration
|
|
202
|
+
* @returns Confidential token account
|
|
203
|
+
*/
|
|
204
|
+
async getOrCreateAccount(
|
|
205
|
+
owner: string,
|
|
206
|
+
token: CSPLToken
|
|
207
|
+
): Promise<ConfidentialTokenAccount> {
|
|
208
|
+
// Validate owner address format
|
|
209
|
+
if (!owner || !isValidSolanaAddressFormat(owner)) {
|
|
210
|
+
throw new Error('Invalid owner address format. Expected base58-encoded Solana address (32-44 chars)')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check cache first
|
|
214
|
+
const cacheKey = `${owner}:${token.confidentialMint}`
|
|
215
|
+
const cached = this.accountCache.get(cacheKey)
|
|
216
|
+
if (cached) {
|
|
217
|
+
return cached
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Validate inputs
|
|
221
|
+
if (!owner || owner.trim() === '') {
|
|
222
|
+
throw new Error('Owner address is required')
|
|
223
|
+
}
|
|
224
|
+
if (!token.confidentialMint || token.confidentialMint.trim() === '') {
|
|
225
|
+
throw new Error('Token confidentialMint is required')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// In production: Query or create account via Solana
|
|
229
|
+
// const ata = getAssociatedTokenAddress(token.confidentialMint, owner)
|
|
230
|
+
// let accountInfo = await connection.getAccountInfo(ata)
|
|
231
|
+
// if (!accountInfo) {
|
|
232
|
+
// await createConfidentialTokenAccount(...)
|
|
233
|
+
// }
|
|
234
|
+
|
|
235
|
+
// Simulated account
|
|
236
|
+
const account: ConfidentialTokenAccount = {
|
|
237
|
+
address: this.deriveAccountAddress(owner, token),
|
|
238
|
+
owner,
|
|
239
|
+
token,
|
|
240
|
+
encryptedBalance: new Uint8Array(64), // Empty encrypted balance
|
|
241
|
+
pendingBalance: undefined,
|
|
242
|
+
pendingCount: 0,
|
|
243
|
+
isInitialized: true,
|
|
244
|
+
isFrozen: false,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.accountCache.set(cacheKey, account)
|
|
248
|
+
return account
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get confidential balance for an account
|
|
253
|
+
*
|
|
254
|
+
* @param owner - Account owner address
|
|
255
|
+
* @param token - C-SPL token configuration
|
|
256
|
+
* @returns Confidential balance
|
|
257
|
+
*/
|
|
258
|
+
async getBalance(owner: string, token: CSPLToken): Promise<ConfidentialBalance> {
|
|
259
|
+
// Validate owner address format
|
|
260
|
+
if (!owner || !isValidSolanaAddressFormat(owner)) {
|
|
261
|
+
throw new Error('Invalid owner address format. Expected base58-encoded Solana address (32-44 chars)')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check cache
|
|
265
|
+
const cacheKey = `balance:${owner}:${token.confidentialMint}`
|
|
266
|
+
const cached = this.balanceCache.get(cacheKey)
|
|
267
|
+
if (cached) {
|
|
268
|
+
return cached
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Get or create account
|
|
272
|
+
const account = await this.getOrCreateAccount(owner, token)
|
|
273
|
+
|
|
274
|
+
// In production: Query on-chain balance
|
|
275
|
+
// const accountData = await program.account.confidentialAccount.fetch(account.address)
|
|
276
|
+
|
|
277
|
+
const balance: ConfidentialBalance = {
|
|
278
|
+
token,
|
|
279
|
+
encryptedAmount: account.encryptedBalance,
|
|
280
|
+
pendingBalance: account.pendingBalance,
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.balanceCache.set(cacheKey, balance)
|
|
284
|
+
return balance
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Wrap SPL tokens to C-SPL
|
|
289
|
+
*
|
|
290
|
+
* Converts regular SPL tokens to their confidential version.
|
|
291
|
+
*
|
|
292
|
+
* @param params - Wrap parameters
|
|
293
|
+
* @returns Wrap result
|
|
294
|
+
*/
|
|
295
|
+
async wrapToken(params: WrapTokenParams): Promise<WrapTokenResult> {
|
|
296
|
+
// Validate inputs
|
|
297
|
+
if (!params.mint || params.mint.trim() === '') {
|
|
298
|
+
return {
|
|
299
|
+
success: false,
|
|
300
|
+
error: 'Token mint address is required',
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (!isValidSolanaAddressFormat(params.mint)) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: 'Invalid token mint address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!params.owner || params.owner.trim() === '') {
|
|
310
|
+
return {
|
|
311
|
+
success: false,
|
|
312
|
+
error: 'Owner address is required',
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (!isValidSolanaAddressFormat(params.owner)) {
|
|
316
|
+
return {
|
|
317
|
+
success: false,
|
|
318
|
+
error: 'Invalid owner address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (params.amount <= BigInt(0)) {
|
|
322
|
+
return {
|
|
323
|
+
success: false,
|
|
324
|
+
error: 'Amount must be greater than 0',
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
// Look up or create C-SPL token config
|
|
330
|
+
const token = this.getOrCreateTokenConfig(params.mint)
|
|
331
|
+
|
|
332
|
+
// Ensure confidential account exists
|
|
333
|
+
if (params.createAccount !== false) {
|
|
334
|
+
await this.getOrCreateAccount(params.owner, token)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// In production:
|
|
338
|
+
// 1. Transfer SPL tokens to wrap escrow
|
|
339
|
+
// 2. Encrypt the amount
|
|
340
|
+
// 3. Credit confidential account
|
|
341
|
+
|
|
342
|
+
const encryptedBalance = await this.encryptAmount({
|
|
343
|
+
amount: params.amount,
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Simulated result
|
|
347
|
+
const signature = this.generateSignature()
|
|
348
|
+
|
|
349
|
+
// Update cache
|
|
350
|
+
const cacheKey = `balance:${params.owner}:${token.confidentialMint}`
|
|
351
|
+
this.balanceCache.set(cacheKey, {
|
|
352
|
+
token,
|
|
353
|
+
encryptedAmount: encryptedBalance.ciphertext,
|
|
354
|
+
decryptedAmount: params.amount,
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
signature,
|
|
360
|
+
token,
|
|
361
|
+
encryptedBalance: encryptedBalance.ciphertext,
|
|
362
|
+
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
error: error instanceof Error ? error.message : 'Unknown error during wrap',
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Unwrap C-SPL back to regular SPL tokens
|
|
373
|
+
*
|
|
374
|
+
* Converts confidential tokens back to their public version.
|
|
375
|
+
*
|
|
376
|
+
* @param params - Unwrap parameters
|
|
377
|
+
* @returns Unwrap result
|
|
378
|
+
*/
|
|
379
|
+
async unwrapToken(params: UnwrapTokenParams): Promise<UnwrapTokenResult> {
|
|
380
|
+
// Validate inputs
|
|
381
|
+
if (!params.token || !params.token.confidentialMint) {
|
|
382
|
+
return {
|
|
383
|
+
success: false,
|
|
384
|
+
error: 'Token configuration is required',
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (!params.owner || params.owner.trim() === '') {
|
|
388
|
+
return {
|
|
389
|
+
success: false,
|
|
390
|
+
error: 'Owner address is required',
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (!isValidSolanaAddressFormat(params.owner)) {
|
|
394
|
+
return {
|
|
395
|
+
success: false,
|
|
396
|
+
error: 'Invalid owner address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!params.encryptedAmount || params.encryptedAmount.length === 0) {
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
error: 'Encrypted amount is required',
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
// In production:
|
|
408
|
+
// 1. Verify ownership and balance
|
|
409
|
+
// 2. Generate range proof
|
|
410
|
+
// 3. Debit confidential account
|
|
411
|
+
// 4. Transfer SPL tokens from escrow
|
|
412
|
+
|
|
413
|
+
// Simulated decryption
|
|
414
|
+
const amount = await this.decryptAmount({
|
|
415
|
+
encryptedAmount: params.encryptedAmount,
|
|
416
|
+
decryptionKey: new Uint8Array(32), // Would be owner's key
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
const signature = this.generateSignature()
|
|
420
|
+
|
|
421
|
+
// Clear balance cache
|
|
422
|
+
const cacheKey = `balance:${params.owner}:${params.token.confidentialMint}`
|
|
423
|
+
this.balanceCache.delete(cacheKey)
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
success: true,
|
|
427
|
+
signature,
|
|
428
|
+
amount,
|
|
429
|
+
}
|
|
430
|
+
} catch (error) {
|
|
431
|
+
return {
|
|
432
|
+
success: false,
|
|
433
|
+
error: error instanceof Error ? error.message : 'Unknown error during unwrap',
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Execute a confidential transfer
|
|
440
|
+
*
|
|
441
|
+
* Transfers C-SPL tokens with encrypted amounts.
|
|
442
|
+
*
|
|
443
|
+
* @param params - Transfer parameters
|
|
444
|
+
* @returns Transfer result
|
|
445
|
+
*/
|
|
446
|
+
async transfer(
|
|
447
|
+
params: ConfidentialTransferParams
|
|
448
|
+
): Promise<ConfidentialTransferResult> {
|
|
449
|
+
// Validate inputs
|
|
450
|
+
if (!params.from || params.from.trim() === '') {
|
|
451
|
+
return {
|
|
452
|
+
success: false,
|
|
453
|
+
error: 'Sender address is required',
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (!isValidSolanaAddressFormat(params.from)) {
|
|
457
|
+
return {
|
|
458
|
+
success: false,
|
|
459
|
+
error: 'Invalid sender address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (!params.to || params.to.trim() === '') {
|
|
463
|
+
return {
|
|
464
|
+
success: false,
|
|
465
|
+
error: 'Recipient address is required',
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (!isValidSolanaAddressFormat(params.to)) {
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
error: 'Invalid recipient address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (!params.token || !params.token.confidentialMint) {
|
|
475
|
+
return {
|
|
476
|
+
success: false,
|
|
477
|
+
error: 'Token configuration is required',
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (!params.encryptedAmount || params.encryptedAmount.length === 0) {
|
|
481
|
+
return {
|
|
482
|
+
success: false,
|
|
483
|
+
error: 'Encrypted amount is required',
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Validate memo length (if provided)
|
|
488
|
+
if (params.memo !== undefined) {
|
|
489
|
+
const memoBytes = new TextEncoder().encode(params.memo)
|
|
490
|
+
if (memoBytes.length > CSPL_MAX_MEMO_BYTES) {
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: `Memo exceeds maximum length (${memoBytes.length} bytes > ${CSPL_MAX_MEMO_BYTES} bytes limit)`,
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
// Ensure both accounts exist
|
|
500
|
+
await this.getOrCreateAccount(params.from, params.token)
|
|
501
|
+
await this.getOrCreateAccount(params.to, params.token)
|
|
502
|
+
|
|
503
|
+
// In production:
|
|
504
|
+
// 1. Generate equality and range proofs
|
|
505
|
+
// 2. Debit sender's confidential balance
|
|
506
|
+
// 3. Credit recipient's pending balance
|
|
507
|
+
// 4. Submit transaction
|
|
508
|
+
|
|
509
|
+
const signature = this.generateSignature()
|
|
510
|
+
|
|
511
|
+
// Simulate new sender balance (would be calculated from proofs)
|
|
512
|
+
const newSenderBalance = new Uint8Array(64)
|
|
513
|
+
|
|
514
|
+
// Clear cache for both parties
|
|
515
|
+
this.balanceCache.delete(`balance:${params.from}:${params.token.confidentialMint}`)
|
|
516
|
+
this.balanceCache.delete(`balance:${params.to}:${params.token.confidentialMint}`)
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
success: true,
|
|
520
|
+
signature,
|
|
521
|
+
newSenderBalance,
|
|
522
|
+
recipientPendingUpdated: true,
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
error: error instanceof Error ? error.message : 'Unknown error during transfer',
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Encrypt an amount for transfer
|
|
534
|
+
*
|
|
535
|
+
* Uses Twisted ElGamal or AES-GCM depending on configuration.
|
|
536
|
+
*
|
|
537
|
+
* @param params - Encryption parameters
|
|
538
|
+
* @returns Encrypted amount
|
|
539
|
+
*/
|
|
540
|
+
async encryptAmount(params: CSPLEncryptionParams): Promise<EncryptedAmount> {
|
|
541
|
+
// Validate amount
|
|
542
|
+
if (params.amount < BigInt(0)) {
|
|
543
|
+
throw new Error('Amount cannot be negative')
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Validate recipient pubkey if provided
|
|
547
|
+
if (params.recipientPubkey !== undefined && params.recipientPubkey !== '') {
|
|
548
|
+
if (!isValidSolanaAddressFormat(params.recipientPubkey)) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
`Invalid recipient pubkey format: '${params.recipientPubkey}'. ` +
|
|
551
|
+
'Expected base58-encoded Solana address (32-44 chars)'
|
|
552
|
+
)
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Validate auditor keys if provided
|
|
557
|
+
if (params.auditorKeys?.length) {
|
|
558
|
+
for (let i = 0; i < params.auditorKeys.length; i++) {
|
|
559
|
+
const auditorKey = params.auditorKeys[i]
|
|
560
|
+
if (!auditorKey || auditorKey.trim() === '') {
|
|
561
|
+
throw new Error(`Auditor key at index ${i} is empty`)
|
|
562
|
+
}
|
|
563
|
+
if (!isValidSolanaAddressFormat(auditorKey)) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
`Invalid auditor key format at index ${i}: '${auditorKey}'. ` +
|
|
566
|
+
'Expected base58-encoded Solana address (32-44 chars)'
|
|
567
|
+
)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const encryptionType = this.config.defaultEncryption
|
|
573
|
+
|
|
574
|
+
// In production: Use actual cryptographic encryption
|
|
575
|
+
// For Twisted ElGamal (Solana Confidential Transfers):
|
|
576
|
+
// const ciphertext = twistedElgamal.encrypt(amount, recipientPubkey)
|
|
577
|
+
|
|
578
|
+
// Simulated encryption
|
|
579
|
+
const ciphertext = this.simulateEncryption(params.amount, encryptionType)
|
|
580
|
+
const nonce = this.generateNonce()
|
|
581
|
+
|
|
582
|
+
// Handle auditor ciphertexts if compliance enabled
|
|
583
|
+
let auditorCiphertexts: Map<string, Uint8Array> | undefined
|
|
584
|
+
if (this.config.enableCompliance && params.auditorKeys?.length) {
|
|
585
|
+
auditorCiphertexts = new Map()
|
|
586
|
+
for (const auditorKey of params.auditorKeys) {
|
|
587
|
+
const auditorCiphertext = this.simulateEncryption(params.amount, 'aes-gcm')
|
|
588
|
+
auditorCiphertexts.set(auditorKey, auditorCiphertext)
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
ciphertext,
|
|
594
|
+
encryptionType,
|
|
595
|
+
nonce,
|
|
596
|
+
auditorCiphertexts,
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Decrypt an encrypted amount
|
|
602
|
+
*
|
|
603
|
+
* Only the account owner can decrypt their balance.
|
|
604
|
+
*
|
|
605
|
+
* @param params - Decryption parameters
|
|
606
|
+
* @returns Decrypted amount
|
|
607
|
+
*/
|
|
608
|
+
async decryptAmount(params: CSPLDecryptionParams): Promise<bigint> {
|
|
609
|
+
// Validate
|
|
610
|
+
if (!params.encryptedAmount || params.encryptedAmount.length === 0) {
|
|
611
|
+
throw new Error('Encrypted amount is required')
|
|
612
|
+
}
|
|
613
|
+
if (!params.decryptionKey || params.decryptionKey.length === 0) {
|
|
614
|
+
throw new Error('Decryption key is required')
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// In production: Use actual cryptographic decryption
|
|
618
|
+
// const amount = twistedElgamal.decrypt(encryptedAmount, decryptionKey)
|
|
619
|
+
|
|
620
|
+
// Simulated decryption - return a deterministic value based on ciphertext
|
|
621
|
+
return this.simulateDecryption(params.encryptedAmount)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Apply pending balance to available balance
|
|
626
|
+
*
|
|
627
|
+
* Required after receiving transfers to make funds spendable.
|
|
628
|
+
*
|
|
629
|
+
* @param owner - Account owner
|
|
630
|
+
* @param token - C-SPL token
|
|
631
|
+
* @returns Transfer result
|
|
632
|
+
*/
|
|
633
|
+
async applyPendingBalance(
|
|
634
|
+
owner: string,
|
|
635
|
+
token: CSPLToken
|
|
636
|
+
): Promise<ConfidentialTransferResult> {
|
|
637
|
+
// Validate
|
|
638
|
+
if (!owner || owner.trim() === '') {
|
|
639
|
+
return {
|
|
640
|
+
success: false,
|
|
641
|
+
error: 'Owner address is required',
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (!isValidSolanaAddressFormat(owner)) {
|
|
645
|
+
return {
|
|
646
|
+
success: false,
|
|
647
|
+
error: 'Invalid owner address format. Expected base58-encoded Solana address (32-44 chars)',
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (!token || !token.confidentialMint) {
|
|
651
|
+
return {
|
|
652
|
+
success: false,
|
|
653
|
+
error: 'Token configuration is required',
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (!isValidSolanaAddressFormat(token.confidentialMint)) {
|
|
657
|
+
return {
|
|
658
|
+
success: false,
|
|
659
|
+
error: 'Invalid token confidentialMint format. Expected base58-encoded Solana address (32-44 chars)',
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
const account = await this.getOrCreateAccount(owner, token)
|
|
665
|
+
|
|
666
|
+
if (!account.pendingBalance || account.pendingCount === 0) {
|
|
667
|
+
return {
|
|
668
|
+
success: true,
|
|
669
|
+
newSenderBalance: account.encryptedBalance,
|
|
670
|
+
recipientPendingUpdated: false,
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// In production:
|
|
675
|
+
// 1. Combine pending balance with available balance
|
|
676
|
+
// 2. Clear pending balance
|
|
677
|
+
// 3. Submit transaction
|
|
678
|
+
|
|
679
|
+
const signature = this.generateSignature()
|
|
680
|
+
|
|
681
|
+
// Update cache
|
|
682
|
+
const cacheKey = `${owner}:${token.confidentialMint}`
|
|
683
|
+
const updatedAccount: ConfidentialTokenAccount = {
|
|
684
|
+
...account,
|
|
685
|
+
pendingBalance: undefined,
|
|
686
|
+
pendingCount: 0,
|
|
687
|
+
}
|
|
688
|
+
this.accountCache.set(cacheKey, updatedAccount)
|
|
689
|
+
|
|
690
|
+
return {
|
|
691
|
+
success: true,
|
|
692
|
+
signature,
|
|
693
|
+
newSenderBalance: updatedAccount.encryptedBalance,
|
|
694
|
+
recipientPendingUpdated: true,
|
|
695
|
+
}
|
|
696
|
+
} catch (error) {
|
|
697
|
+
return {
|
|
698
|
+
success: false,
|
|
699
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ─── Query Methods ────────────────────────────────────────────────────────────
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Get token configuration for a known mint
|
|
708
|
+
*
|
|
709
|
+
* @param symbol - Token symbol (e.g., 'C-USDC')
|
|
710
|
+
* @returns Token configuration or undefined
|
|
711
|
+
*/
|
|
712
|
+
getKnownToken(symbol: string): Partial<CSPLToken> | undefined {
|
|
713
|
+
return CSPL_TOKENS[symbol]
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* List all known C-SPL tokens
|
|
718
|
+
*/
|
|
719
|
+
listKnownTokens(): string[] {
|
|
720
|
+
return Object.keys(CSPL_TOKENS)
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Estimate cost for an operation
|
|
725
|
+
*
|
|
726
|
+
* Returns the estimated cost in lamports. Made async for consistency
|
|
727
|
+
* with other privacy backend cost estimation methods.
|
|
728
|
+
*
|
|
729
|
+
* @param operation - Operation type
|
|
730
|
+
* @returns Estimated cost in lamports
|
|
731
|
+
*/
|
|
732
|
+
async estimateCost(operation: keyof typeof CSPL_OPERATION_COSTS): Promise<bigint> {
|
|
733
|
+
return CSPL_OPERATION_COSTS[operation]
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Estimate time for an operation
|
|
738
|
+
*
|
|
739
|
+
* Returns the estimated time in milliseconds. Made async for consistency
|
|
740
|
+
* with other privacy backend estimation methods.
|
|
741
|
+
*
|
|
742
|
+
* @param operation - Operation type
|
|
743
|
+
* @returns Estimated time in milliseconds
|
|
744
|
+
*/
|
|
745
|
+
async estimateTime(operation: keyof typeof CSPL_OPERATION_TIMES): Promise<number> {
|
|
746
|
+
return CSPL_OPERATION_TIMES[operation]
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Get current configuration (deeply frozen copy)
|
|
751
|
+
*/
|
|
752
|
+
getConfig(): Readonly<CSPLClientConfig> {
|
|
753
|
+
return deepFreeze({ ...this.config })
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Get program IDs
|
|
758
|
+
*/
|
|
759
|
+
getProgramIds(): typeof CSPL_PROGRAM_IDS {
|
|
760
|
+
return CSPL_PROGRAM_IDS
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ─── Private Methods ──────────────────────────────────────────────────────────
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Derive confidential account address
|
|
767
|
+
*/
|
|
768
|
+
private deriveAccountAddress(owner: string, token: CSPLToken): string {
|
|
769
|
+
// In production: Calculate actual PDA
|
|
770
|
+
// return PublicKey.findProgramAddress(...)
|
|
771
|
+
const hash = this.simpleHash(`${owner}:${token.confidentialMint}`)
|
|
772
|
+
return `cspl_${hash}`
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Get or create token configuration
|
|
777
|
+
*/
|
|
778
|
+
private getOrCreateTokenConfig(mint: string): CSPLToken {
|
|
779
|
+
// Check known tokens
|
|
780
|
+
for (const [, config] of Object.entries(CSPL_TOKENS)) {
|
|
781
|
+
if (config.mint === mint) {
|
|
782
|
+
return {
|
|
783
|
+
mint,
|
|
784
|
+
confidentialMint: `cspl_${mint.slice(0, 8)}`,
|
|
785
|
+
decimals: config.decimals ?? 9,
|
|
786
|
+
symbol: config.symbol,
|
|
787
|
+
name: config.name,
|
|
788
|
+
isNativeWrap: config.isNativeWrap,
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Create generic config for unknown token
|
|
794
|
+
return {
|
|
795
|
+
mint,
|
|
796
|
+
confidentialMint: `cspl_${mint.slice(0, 8)}`,
|
|
797
|
+
decimals: 9, // Default to 9 decimals
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Generate a simulated transaction signature
|
|
803
|
+
*/
|
|
804
|
+
private generateSignature(): string {
|
|
805
|
+
const timestamp = Date.now().toString(36)
|
|
806
|
+
const random = Math.random().toString(36).slice(2, 10)
|
|
807
|
+
return `cspl_tx_${timestamp}_${random}`
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Generate a nonce for encryption
|
|
812
|
+
*/
|
|
813
|
+
private generateNonce(): Uint8Array {
|
|
814
|
+
const nonce = new Uint8Array(12)
|
|
815
|
+
for (let i = 0; i < 12; i++) {
|
|
816
|
+
nonce[i] = Math.floor(Math.random() * 256)
|
|
817
|
+
}
|
|
818
|
+
return nonce
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Simulate encryption (for testing without crypto libs)
|
|
823
|
+
*/
|
|
824
|
+
private simulateEncryption(amount: bigint, type: CSPLEncryptionType): Uint8Array {
|
|
825
|
+
const encoder = new TextEncoder()
|
|
826
|
+
const combined = `${type}:${amount.toString()}:${Date.now()}`
|
|
827
|
+
return encoder.encode(combined)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Simulate decryption (for testing)
|
|
832
|
+
*/
|
|
833
|
+
private simulateDecryption(ciphertext: Uint8Array): bigint {
|
|
834
|
+
// Deterministic but fake - return hash-based value
|
|
835
|
+
let sum = 0
|
|
836
|
+
for (const byte of ciphertext) {
|
|
837
|
+
sum += byte
|
|
838
|
+
}
|
|
839
|
+
return BigInt(sum % 1_000_000_000)
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Simple hash for deterministic addresses
|
|
844
|
+
*/
|
|
845
|
+
private simpleHash(input: string): string {
|
|
846
|
+
let hash = 0
|
|
847
|
+
for (let i = 0; i < input.length; i++) {
|
|
848
|
+
const char = input.charCodeAt(i)
|
|
849
|
+
hash = ((hash << 5) - hash) + char
|
|
850
|
+
hash = hash & hash
|
|
851
|
+
}
|
|
852
|
+
return Math.abs(hash).toString(36)
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Clear all caches
|
|
857
|
+
*/
|
|
858
|
+
clearCache(): void {
|
|
859
|
+
this.accountCache.clear()
|
|
860
|
+
this.balanceCache.clear()
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Get cache entry counts (backward compatible)
|
|
865
|
+
*
|
|
866
|
+
* @returns Number of entries in each cache
|
|
867
|
+
*/
|
|
868
|
+
getCacheStats(): { accounts: number; balances: number } {
|
|
869
|
+
return {
|
|
870
|
+
accounts: this.accountCache.size,
|
|
871
|
+
balances: this.balanceCache.size,
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Get detailed cache statistics including LRU metrics
|
|
877
|
+
*
|
|
878
|
+
* @returns Detailed cache stats with hit rates and eviction counts
|
|
879
|
+
*/
|
|
880
|
+
getDetailedCacheStats(): CSPLCacheStats {
|
|
881
|
+
return {
|
|
882
|
+
accounts: this.accountCache.getStats(),
|
|
883
|
+
balances: this.balanceCache.getStats(),
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Get cache configuration
|
|
889
|
+
*
|
|
890
|
+
* @returns Current cache configuration
|
|
891
|
+
*/
|
|
892
|
+
getCacheConfig(): Readonly<CSPLCacheConfig> {
|
|
893
|
+
return deepFreeze({ ...this.cacheConfig })
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Prune expired entries from all caches
|
|
898
|
+
*
|
|
899
|
+
* @returns Number of entries pruned from each cache
|
|
900
|
+
*/
|
|
901
|
+
pruneExpiredCache(): { accounts: number; balances: number } {
|
|
902
|
+
return {
|
|
903
|
+
accounts: this.accountCache.prune(),
|
|
904
|
+
balances: this.balanceCache.prune(),
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|