@sip-protocol/sdk 0.7.3 → 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 +47556 -19603
- package/dist/browser.mjs +628 -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-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- 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-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -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 +252 -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 +47 -6
- 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 +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- 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 +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- 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 +57 -6
- 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 +23 -0
- package/src/compliance/range-sas.ts +398 -33
- 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 +686 -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 +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- 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.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -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 +110 -29
- 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/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-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- 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-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy-Extended Ethereum Wallet Adapter
|
|
3
|
+
*
|
|
4
|
+
* Extends EthereumWalletAdapter with stealth address capabilities for
|
|
5
|
+
* same-chain privacy operations using secp256k1 curve.
|
|
6
|
+
*
|
|
7
|
+
* @module wallet/ethereum/privacy-adapter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
HexString,
|
|
12
|
+
StealthMetaAddress,
|
|
13
|
+
StealthAddress,
|
|
14
|
+
} from '@sip-protocol/types'
|
|
15
|
+
import { sha256 } from '@noble/hashes/sha2'
|
|
16
|
+
import { secp256k1 } from '@noble/curves/secp256k1'
|
|
17
|
+
import { keccak_256 } from '@noble/hashes/sha3'
|
|
18
|
+
import { EthereumWalletAdapter } from './adapter'
|
|
19
|
+
import type { EthereumAdapterConfig, EIP712TypedData } from './types'
|
|
20
|
+
import {
|
|
21
|
+
generateEthereumStealthMetaAddress,
|
|
22
|
+
generateEthereumStealthAddress,
|
|
23
|
+
deriveEthereumStealthPrivateKey,
|
|
24
|
+
checkEthereumStealthAddress,
|
|
25
|
+
stealthPublicKeyToEthAddress,
|
|
26
|
+
encodeEthereumStealthMetaAddress,
|
|
27
|
+
parseEthereumStealthMetaAddress,
|
|
28
|
+
type EthereumStealthMetaAddress,
|
|
29
|
+
} from '../../chains/ethereum/stealth'
|
|
30
|
+
|
|
31
|
+
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for privacy adapter
|
|
35
|
+
*/
|
|
36
|
+
export interface PrivacyEthereumAdapterConfig extends EthereumAdapterConfig {
|
|
37
|
+
/**
|
|
38
|
+
* Pre-existing stealth meta-address to use
|
|
39
|
+
* If not provided, one will be generated on first use
|
|
40
|
+
*/
|
|
41
|
+
metaAddress?: StealthMetaAddress | EthereumStealthMetaAddress | string
|
|
42
|
+
/**
|
|
43
|
+
* Pre-existing spending private key
|
|
44
|
+
* Required if metaAddress is provided
|
|
45
|
+
*/
|
|
46
|
+
spendingPrivateKey?: HexString
|
|
47
|
+
/**
|
|
48
|
+
* Pre-existing viewing private key
|
|
49
|
+
* Required if metaAddress is provided
|
|
50
|
+
*/
|
|
51
|
+
viewingPrivateKey?: HexString
|
|
52
|
+
/**
|
|
53
|
+
* Derive stealth keys from wallet signature
|
|
54
|
+
* If true, keys are derived deterministically from wallet
|
|
55
|
+
* If false (default), random keys are generated
|
|
56
|
+
*/
|
|
57
|
+
deriveFromWallet?: boolean
|
|
58
|
+
/**
|
|
59
|
+
* Domain separation string for key derivation
|
|
60
|
+
* Used when deriveFromWallet is true
|
|
61
|
+
*/
|
|
62
|
+
derivationDomain?: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Stealth key material for Ethereum
|
|
67
|
+
*/
|
|
68
|
+
export interface EthereumStealthKeyMaterial {
|
|
69
|
+
metaAddress: StealthMetaAddress
|
|
70
|
+
spendingPrivateKey: HexString
|
|
71
|
+
viewingPrivateKey: HexString
|
|
72
|
+
/** Encoded EIP-5564 format */
|
|
73
|
+
encodedMetaAddress: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Scanned stealth payment
|
|
78
|
+
*/
|
|
79
|
+
export interface EthereumScannedPayment {
|
|
80
|
+
/** Stealth address that received the payment */
|
|
81
|
+
stealthAddress: HexString
|
|
82
|
+
/** Ephemeral public key from sender */
|
|
83
|
+
ephemeralPublicKey: HexString
|
|
84
|
+
/** View tag for fast scanning */
|
|
85
|
+
viewTag: number
|
|
86
|
+
/** Whether this payment belongs to us */
|
|
87
|
+
isOwned: boolean
|
|
88
|
+
/** Ethereum address format of stealth address */
|
|
89
|
+
ethAddress: HexString
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Result of claiming a stealth payment
|
|
94
|
+
*/
|
|
95
|
+
export interface EthereumClaimResult {
|
|
96
|
+
/** Derived private key for spending */
|
|
97
|
+
privateKey: HexString
|
|
98
|
+
/** Public key (compressed) */
|
|
99
|
+
publicKey: HexString
|
|
100
|
+
/** Ethereum address */
|
|
101
|
+
ethAddress: HexString
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Privacy context that persists across reconnections
|
|
106
|
+
*/
|
|
107
|
+
export interface PrivacyContext {
|
|
108
|
+
/** Current stealth keys */
|
|
109
|
+
keys: EthereumStealthKeyMaterial
|
|
110
|
+
/** Whether keys were derived from wallet */
|
|
111
|
+
derivedFromWallet: boolean
|
|
112
|
+
/** Derivation domain if applicable */
|
|
113
|
+
derivationDomain?: string
|
|
114
|
+
/** Wallet address keys were derived for */
|
|
115
|
+
derivedForAddress?: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Privacy Adapter ────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Privacy-enabled Ethereum wallet adapter
|
|
122
|
+
*
|
|
123
|
+
* Extends the base EthereumWalletAdapter with:
|
|
124
|
+
* - Stealth meta-address generation and management
|
|
125
|
+
* - Stealth address derivation for receiving
|
|
126
|
+
* - Private key derivation for spending
|
|
127
|
+
* - Payment scanning with view tags
|
|
128
|
+
* - EIP-712 signing for permit operations
|
|
129
|
+
*
|
|
130
|
+
* @example Generate stealth address for receiving
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const wallet = new PrivacyEthereumWalletAdapter({ wallet: 'metamask' })
|
|
133
|
+
* await wallet.connect()
|
|
134
|
+
* await wallet.initializePrivacy()
|
|
135
|
+
*
|
|
136
|
+
* // Share meta-address with senders
|
|
137
|
+
* const metaAddress = wallet.getMetaAddress()
|
|
138
|
+
* console.log('Send to:', wallet.getEncodedMetaAddress())
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @example Derive from wallet (deterministic)
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const wallet = new PrivacyEthereumWalletAdapter({
|
|
144
|
+
* wallet: 'metamask',
|
|
145
|
+
* deriveFromWallet: true,
|
|
146
|
+
* derivationDomain: 'my-app.com',
|
|
147
|
+
* })
|
|
148
|
+
* await wallet.connect()
|
|
149
|
+
* await wallet.initializePrivacy()
|
|
150
|
+
* // Keys derived deterministically from wallet signature
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @example Scan and claim payments
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const payments = wallet.scanPayments([announcement1, announcement2])
|
|
156
|
+
* for (const payment of payments.filter(p => p.isOwned)) {
|
|
157
|
+
* const claim = wallet.deriveClaimKey(payment.ephemeralPublicKey, payment.viewTag)
|
|
158
|
+
* // Use claim.privateKey to sign transactions
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export class PrivacyEthereumWalletAdapter extends EthereumWalletAdapter {
|
|
163
|
+
private stealthKeys: EthereumStealthKeyMaterial | undefined
|
|
164
|
+
private deriveFromWallet: boolean
|
|
165
|
+
private derivationDomain: string
|
|
166
|
+
private privacyContext: PrivacyContext | undefined
|
|
167
|
+
|
|
168
|
+
constructor(config: PrivacyEthereumAdapterConfig = {}) {
|
|
169
|
+
super(config)
|
|
170
|
+
|
|
171
|
+
this.deriveFromWallet = config.deriveFromWallet ?? false
|
|
172
|
+
this.derivationDomain = config.derivationDomain ?? 'sip-protocol.org'
|
|
173
|
+
|
|
174
|
+
// If pre-existing keys provided, validate and store
|
|
175
|
+
if (config.metaAddress) {
|
|
176
|
+
if (!config.spendingPrivateKey || !config.viewingPrivateKey) {
|
|
177
|
+
throw new Error('spendingPrivateKey and viewingPrivateKey required when metaAddress provided')
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Parse meta-address if string
|
|
181
|
+
let metaAddress: StealthMetaAddress
|
|
182
|
+
if (typeof config.metaAddress === 'string') {
|
|
183
|
+
const parsed = parseEthereumStealthMetaAddress(config.metaAddress)
|
|
184
|
+
metaAddress = {
|
|
185
|
+
chain: 'ethereum',
|
|
186
|
+
spendingKey: parsed.spendingKey,
|
|
187
|
+
viewingKey: parsed.viewingKey,
|
|
188
|
+
}
|
|
189
|
+
} else if ('spendingKey' in config.metaAddress) {
|
|
190
|
+
// Already StealthMetaAddress format
|
|
191
|
+
metaAddress = config.metaAddress as StealthMetaAddress
|
|
192
|
+
} else {
|
|
193
|
+
throw new Error('Invalid meta-address format')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.stealthKeys = {
|
|
197
|
+
metaAddress,
|
|
198
|
+
spendingPrivateKey: config.spendingPrivateKey,
|
|
199
|
+
viewingPrivateKey: config.viewingPrivateKey,
|
|
200
|
+
encodedMetaAddress: encodeEthereumStealthMetaAddress(metaAddress),
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Initialize privacy features
|
|
207
|
+
*
|
|
208
|
+
* Generates or derives stealth keys. Must be called after connect().
|
|
209
|
+
* If deriveFromWallet is true, will prompt for a signature.
|
|
210
|
+
*/
|
|
211
|
+
async initializePrivacy(): Promise<void> {
|
|
212
|
+
this.requireConnected()
|
|
213
|
+
|
|
214
|
+
// If we have a privacy context for this address, restore it
|
|
215
|
+
if (this.privacyContext && this.privacyContext.derivedForAddress === this.address) {
|
|
216
|
+
this.stealthKeys = this.privacyContext.keys
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (this.stealthKeys) {
|
|
221
|
+
return // Already initialized
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (this.deriveFromWallet) {
|
|
225
|
+
await this.deriveStealthKeysFromWallet()
|
|
226
|
+
} else {
|
|
227
|
+
this.generateRandomStealthKeys()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if privacy is initialized
|
|
233
|
+
*/
|
|
234
|
+
isPrivacyInitialized(): boolean {
|
|
235
|
+
return !!this.stealthKeys
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get the stealth meta-address
|
|
240
|
+
*
|
|
241
|
+
* Share this with senders to receive private payments.
|
|
242
|
+
*
|
|
243
|
+
* @throws If privacy not initialized
|
|
244
|
+
*/
|
|
245
|
+
getMetaAddress(): StealthMetaAddress {
|
|
246
|
+
if (!this.stealthKeys) {
|
|
247
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
248
|
+
}
|
|
249
|
+
return this.stealthKeys.metaAddress
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get the encoded EIP-5564 meta-address string
|
|
254
|
+
*
|
|
255
|
+
* Format: st:eth:0x<spendingKey><viewingKey>
|
|
256
|
+
*
|
|
257
|
+
* @throws If privacy not initialized
|
|
258
|
+
*/
|
|
259
|
+
getEncodedMetaAddress(): string {
|
|
260
|
+
if (!this.stealthKeys) {
|
|
261
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
262
|
+
}
|
|
263
|
+
return this.stealthKeys.encodedMetaAddress
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get the viewing private key
|
|
268
|
+
*
|
|
269
|
+
* Used for scanning incoming payments.
|
|
270
|
+
* Can be shared with auditors for compliance.
|
|
271
|
+
*
|
|
272
|
+
* @throws If privacy not initialized
|
|
273
|
+
*/
|
|
274
|
+
getViewingPrivateKey(): HexString {
|
|
275
|
+
if (!this.stealthKeys) {
|
|
276
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
277
|
+
}
|
|
278
|
+
return this.stealthKeys.viewingPrivateKey
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get the spending private key
|
|
283
|
+
*
|
|
284
|
+
* SENSITIVE: Required for deriving stealth private keys.
|
|
285
|
+
* Keep secure and never share.
|
|
286
|
+
*
|
|
287
|
+
* @throws If privacy not initialized
|
|
288
|
+
*/
|
|
289
|
+
getSpendingPrivateKey(): HexString {
|
|
290
|
+
if (!this.stealthKeys) {
|
|
291
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
292
|
+
}
|
|
293
|
+
return this.stealthKeys.spendingPrivateKey
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generate a one-time stealth address for a sender
|
|
298
|
+
*
|
|
299
|
+
* Typically used by the sender side. Recipients share their meta-address,
|
|
300
|
+
* and senders generate unique stealth addresses for each payment.
|
|
301
|
+
*
|
|
302
|
+
* @param recipientMetaAddress - Recipient's stealth meta-address
|
|
303
|
+
* @returns Stealth address details for the transaction
|
|
304
|
+
*/
|
|
305
|
+
generateStealthAddressFor(recipientMetaAddress: StealthMetaAddress | string): {
|
|
306
|
+
stealthAddress: StealthAddress
|
|
307
|
+
ethAddress: HexString
|
|
308
|
+
sharedSecret: HexString
|
|
309
|
+
} {
|
|
310
|
+
const { stealthAddress, sharedSecret } = generateEthereumStealthAddress(recipientMetaAddress)
|
|
311
|
+
const ethAddress = stealthPublicKeyToEthAddress(stealthAddress.address)
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
stealthAddress,
|
|
315
|
+
ethAddress,
|
|
316
|
+
sharedSecret,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Scan announcements to find payments belonging to us
|
|
322
|
+
*
|
|
323
|
+
* Uses the viewing key to check if stealth addresses belong to this wallet.
|
|
324
|
+
* First checks view tags for fast filtering, then verifies ownership.
|
|
325
|
+
*
|
|
326
|
+
* @param announcements - Array of stealth address announcements to scan
|
|
327
|
+
* @returns Array of payments with ownership status
|
|
328
|
+
*/
|
|
329
|
+
scanPayments(announcements: StealthAddress[]): EthereumScannedPayment[] {
|
|
330
|
+
if (!this.stealthKeys) {
|
|
331
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const results: EthereumScannedPayment[] = []
|
|
335
|
+
|
|
336
|
+
for (const announcement of announcements) {
|
|
337
|
+
try {
|
|
338
|
+
const isOwned = checkEthereumStealthAddress(
|
|
339
|
+
announcement,
|
|
340
|
+
this.stealthKeys.metaAddress.spendingKey,
|
|
341
|
+
this.stealthKeys.viewingPrivateKey,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
let ethAddress: HexString
|
|
345
|
+
try {
|
|
346
|
+
ethAddress = stealthPublicKeyToEthAddress(announcement.address)
|
|
347
|
+
} catch {
|
|
348
|
+
ethAddress = '0x0000000000000000000000000000000000000000' as HexString
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
results.push({
|
|
352
|
+
stealthAddress: announcement.address,
|
|
353
|
+
ephemeralPublicKey: announcement.ephemeralPublicKey,
|
|
354
|
+
viewTag: announcement.viewTag,
|
|
355
|
+
isOwned,
|
|
356
|
+
ethAddress,
|
|
357
|
+
})
|
|
358
|
+
} catch {
|
|
359
|
+
// Invalid announcement, skip
|
|
360
|
+
results.push({
|
|
361
|
+
stealthAddress: announcement.address,
|
|
362
|
+
ephemeralPublicKey: announcement.ephemeralPublicKey,
|
|
363
|
+
viewTag: announcement.viewTag,
|
|
364
|
+
isOwned: false,
|
|
365
|
+
ethAddress: '0x0000000000000000000000000000000000000000' as HexString,
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return results
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Fast scan using view tags only
|
|
375
|
+
*
|
|
376
|
+
* Pre-filters announcements by view tag before full verification.
|
|
377
|
+
* Much faster for large announcement sets.
|
|
378
|
+
*
|
|
379
|
+
* @param announcements - Announcements with view tags
|
|
380
|
+
* @returns Potentially matching announcements
|
|
381
|
+
*/
|
|
382
|
+
fastScanByViewTag(announcements: StealthAddress[]): StealthAddress[] {
|
|
383
|
+
if (!this.stealthKeys) {
|
|
384
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Get viewing private key bytes
|
|
388
|
+
const viewingKeyBytes = hexToBytes(this.stealthKeys.viewingPrivateKey.slice(2))
|
|
389
|
+
|
|
390
|
+
return announcements.filter(ann => {
|
|
391
|
+
try {
|
|
392
|
+
const ephemeralBytes = hexToBytes(ann.ephemeralPublicKey.slice(2))
|
|
393
|
+
|
|
394
|
+
// Compute shared secret point
|
|
395
|
+
const ephemeralPoint = secp256k1.ProjectivePoint.fromHex(ephemeralBytes)
|
|
396
|
+
const sharedPoint = ephemeralPoint.multiply(bytesToBigInt(viewingKeyBytes))
|
|
397
|
+
const sharedSecretBytes = sharedPoint.toRawBytes(true)
|
|
398
|
+
|
|
399
|
+
// Hash shared secret to get view tag
|
|
400
|
+
const hash = keccak_256(sharedSecretBytes)
|
|
401
|
+
const expectedViewTag = hash[0]
|
|
402
|
+
|
|
403
|
+
return ann.viewTag === expectedViewTag
|
|
404
|
+
} catch {
|
|
405
|
+
return false
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Derive the private key for spending from a stealth address
|
|
412
|
+
*
|
|
413
|
+
* @param ephemeralPublicKey - Ephemeral public key from the announcement
|
|
414
|
+
* @param viewTag - View tag from the announcement
|
|
415
|
+
* @returns Claim key material for spending
|
|
416
|
+
*/
|
|
417
|
+
deriveClaimKey(
|
|
418
|
+
ephemeralPublicKey: HexString,
|
|
419
|
+
viewTag: number
|
|
420
|
+
): EthereumClaimResult {
|
|
421
|
+
if (!this.stealthKeys) {
|
|
422
|
+
throw new Error('Privacy not initialized. Call initializePrivacy() first.')
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Create StealthAddress for derivation
|
|
426
|
+
const stealthAddr: StealthAddress = {
|
|
427
|
+
address: '0x' + '00'.repeat(33) as HexString, // Will be computed
|
|
428
|
+
ephemeralPublicKey,
|
|
429
|
+
viewTag,
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Derive the private key using our crypto module
|
|
433
|
+
const recovery = deriveEthereumStealthPrivateKey(
|
|
434
|
+
stealthAddr,
|
|
435
|
+
this.stealthKeys.spendingPrivateKey,
|
|
436
|
+
this.stealthKeys.viewingPrivateKey,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
privateKey: recovery.privateKey,
|
|
441
|
+
publicKey: recovery.stealthAddress,
|
|
442
|
+
ethAddress: recovery.ethAddress,
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Sign EIP-712 typed data for permit or other structured data
|
|
448
|
+
*
|
|
449
|
+
* Convenience method that uses the wallet's signTypedData.
|
|
450
|
+
*
|
|
451
|
+
* @param typedData - EIP-712 typed data
|
|
452
|
+
* @returns Signature
|
|
453
|
+
*/
|
|
454
|
+
async signPermitData(typedData: EIP712TypedData): Promise<{
|
|
455
|
+
v: number
|
|
456
|
+
r: HexString
|
|
457
|
+
s: HexString
|
|
458
|
+
}> {
|
|
459
|
+
const signature = await this.signTypedData(typedData)
|
|
460
|
+
|
|
461
|
+
// Parse signature into v, r, s
|
|
462
|
+
const sig = signature.signature.slice(2) // Remove 0x
|
|
463
|
+
const r = `0x${sig.slice(0, 64)}` as HexString
|
|
464
|
+
const s = `0x${sig.slice(64, 128)}` as HexString
|
|
465
|
+
const v = parseInt(sig.slice(128, 130), 16)
|
|
466
|
+
|
|
467
|
+
return { v, r, s }
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Export stealth keys for backup
|
|
472
|
+
*
|
|
473
|
+
* Returns all key material needed to restore privacy functionality.
|
|
474
|
+
* SENSITIVE: Store securely and never expose.
|
|
475
|
+
*/
|
|
476
|
+
exportStealthKeys(): EthereumStealthKeyMaterial | undefined {
|
|
477
|
+
return this.stealthKeys ? { ...this.stealthKeys } : undefined
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Import stealth keys from backup
|
|
482
|
+
*
|
|
483
|
+
* @param keys - Previously exported stealth keys
|
|
484
|
+
*/
|
|
485
|
+
importStealthKeys(keys: EthereumStealthKeyMaterial): void {
|
|
486
|
+
this.stealthKeys = { ...keys }
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get privacy context for persistence
|
|
491
|
+
*
|
|
492
|
+
* Use this to save privacy state across reconnections.
|
|
493
|
+
*/
|
|
494
|
+
getPrivacyContext(): PrivacyContext | undefined {
|
|
495
|
+
if (!this.stealthKeys) {
|
|
496
|
+
return undefined
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
keys: { ...this.stealthKeys },
|
|
501
|
+
derivedFromWallet: this.deriveFromWallet,
|
|
502
|
+
derivationDomain: this.deriveFromWallet ? this.derivationDomain : undefined,
|
|
503
|
+
derivedForAddress: this.deriveFromWallet ? this.address : undefined,
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Restore privacy context from saved state
|
|
509
|
+
*
|
|
510
|
+
* @param context - Previously saved privacy context
|
|
511
|
+
*/
|
|
512
|
+
setPrivacyContext(context: PrivacyContext): void {
|
|
513
|
+
this.privacyContext = context
|
|
514
|
+
if (this.address === context.derivedForAddress || !context.derivedFromWallet) {
|
|
515
|
+
this.stealthKeys = context.keys
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Clear privacy state
|
|
521
|
+
*
|
|
522
|
+
* Removes all stealth keys from memory.
|
|
523
|
+
*/
|
|
524
|
+
clearPrivacy(): void {
|
|
525
|
+
this.stealthKeys = undefined
|
|
526
|
+
this.privacyContext = undefined
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Generate random stealth keys
|
|
531
|
+
*/
|
|
532
|
+
private generateRandomStealthKeys(): void {
|
|
533
|
+
const { metaAddress, encoded, spendingPrivateKey, viewingPrivateKey } =
|
|
534
|
+
generateEthereumStealthMetaAddress()
|
|
535
|
+
|
|
536
|
+
this.stealthKeys = {
|
|
537
|
+
metaAddress: {
|
|
538
|
+
chain: 'ethereum',
|
|
539
|
+
spendingKey: metaAddress.spendingKey,
|
|
540
|
+
viewingKey: metaAddress.viewingKey,
|
|
541
|
+
},
|
|
542
|
+
spendingPrivateKey,
|
|
543
|
+
viewingPrivateKey,
|
|
544
|
+
encodedMetaAddress: encoded,
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Derive stealth keys deterministically from wallet signature
|
|
550
|
+
*
|
|
551
|
+
* Uses wallet signature over a domain-specific message to derive
|
|
552
|
+
* deterministic stealth keys. Same wallet + domain = same keys.
|
|
553
|
+
*/
|
|
554
|
+
private async deriveStealthKeysFromWallet(): Promise<void> {
|
|
555
|
+
const message = `SIP Protocol Stealth Key Derivation\n` +
|
|
556
|
+
`Domain: ${this.derivationDomain}\n` +
|
|
557
|
+
`Address: ${this.address}\n` +
|
|
558
|
+
`Chain: ethereum\n` +
|
|
559
|
+
`Version: 1`
|
|
560
|
+
|
|
561
|
+
// Sign the derivation message
|
|
562
|
+
const messageBytes = new TextEncoder().encode(message)
|
|
563
|
+
const signature = await this.signMessage(messageBytes)
|
|
564
|
+
|
|
565
|
+
// Use signature as seed for key derivation
|
|
566
|
+
const signatureBytes = hexToBytes(signature.signature.slice(2))
|
|
567
|
+
|
|
568
|
+
// Derive spending key: hash(sig || "spending")
|
|
569
|
+
const spendingInput = new Uint8Array(signatureBytes.length + 8)
|
|
570
|
+
spendingInput.set(signatureBytes)
|
|
571
|
+
spendingInput.set(new TextEncoder().encode('spending'), signatureBytes.length)
|
|
572
|
+
const spendingHash = sha256(spendingInput)
|
|
573
|
+
|
|
574
|
+
// Ensure spending key is valid for secp256k1
|
|
575
|
+
const spendingPrivateKey = ensureValidSecp256k1Key(spendingHash)
|
|
576
|
+
|
|
577
|
+
// Derive viewing key: hash(sig || "viewing")
|
|
578
|
+
const viewingInput = new Uint8Array(signatureBytes.length + 7)
|
|
579
|
+
viewingInput.set(signatureBytes)
|
|
580
|
+
viewingInput.set(new TextEncoder().encode('viewing'), signatureBytes.length)
|
|
581
|
+
const viewingHash = sha256(viewingInput)
|
|
582
|
+
|
|
583
|
+
// Ensure viewing key is valid for secp256k1
|
|
584
|
+
const viewingPrivateKey = ensureValidSecp256k1Key(viewingHash)
|
|
585
|
+
|
|
586
|
+
// Compute public keys (compressed)
|
|
587
|
+
const spendingKey = secp256k1.getPublicKey(spendingPrivateKey, true)
|
|
588
|
+
const viewingKey = secp256k1.getPublicKey(viewingPrivateKey, true)
|
|
589
|
+
|
|
590
|
+
const spendingKeyHex = `0x${bytesToHex(spendingKey)}` as HexString
|
|
591
|
+
const viewingKeyHex = `0x${bytesToHex(viewingKey)}` as HexString
|
|
592
|
+
|
|
593
|
+
const metaAddress: StealthMetaAddress = {
|
|
594
|
+
chain: 'ethereum',
|
|
595
|
+
spendingKey: spendingKeyHex,
|
|
596
|
+
viewingKey: viewingKeyHex,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
this.stealthKeys = {
|
|
600
|
+
metaAddress,
|
|
601
|
+
spendingPrivateKey: `0x${bytesToHex(spendingPrivateKey)}` as HexString,
|
|
602
|
+
viewingPrivateKey: `0x${bytesToHex(viewingPrivateKey)}` as HexString,
|
|
603
|
+
encodedMetaAddress: encodeEthereumStealthMetaAddress(metaAddress),
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Store privacy context
|
|
607
|
+
this.privacyContext = {
|
|
608
|
+
keys: this.stealthKeys,
|
|
609
|
+
derivedFromWallet: true,
|
|
610
|
+
derivationDomain: this.derivationDomain,
|
|
611
|
+
derivedForAddress: this.address,
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ─── Utilities ──────────────────────────────────────────────────────────────
|
|
617
|
+
|
|
618
|
+
/** secp256k1 curve order */
|
|
619
|
+
const SECP256K1_ORDER = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141')
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Convert hex string to bytes
|
|
623
|
+
*/
|
|
624
|
+
function hexToBytes(hex: string): Uint8Array {
|
|
625
|
+
const bytes = new Uint8Array(hex.length / 2)
|
|
626
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
627
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
|
|
628
|
+
}
|
|
629
|
+
return bytes
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Convert bytes to hex string
|
|
634
|
+
*/
|
|
635
|
+
function bytesToHex(bytes: Uint8Array): string {
|
|
636
|
+
return Array.from(bytes)
|
|
637
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
638
|
+
.join('')
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Convert bytes to BigInt (big-endian)
|
|
643
|
+
*/
|
|
644
|
+
function bytesToBigInt(bytes: Uint8Array): bigint {
|
|
645
|
+
let result = 0n
|
|
646
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
647
|
+
result = (result << 8n) | BigInt(bytes[i])
|
|
648
|
+
}
|
|
649
|
+
return result
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Ensure key is valid for secp256k1 (1 < key < curve order)
|
|
654
|
+
*/
|
|
655
|
+
function ensureValidSecp256k1Key(hash: Uint8Array): Uint8Array {
|
|
656
|
+
let key = bytesToBigInt(hash)
|
|
657
|
+
|
|
658
|
+
// Reduce modulo curve order and ensure non-zero
|
|
659
|
+
key = key % SECP256K1_ORDER
|
|
660
|
+
if (key === 0n) {
|
|
661
|
+
key = 1n
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Convert back to bytes
|
|
665
|
+
const result = new Uint8Array(32)
|
|
666
|
+
for (let i = 31; i >= 0; i--) {
|
|
667
|
+
result[i] = Number(key & 0xffn)
|
|
668
|
+
key >>= 8n
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return result
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// ─── Factory ────────────────────────────────────────────────────────────────
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Create a privacy-enabled Ethereum wallet adapter
|
|
678
|
+
*
|
|
679
|
+
* @example Random keys
|
|
680
|
+
* ```typescript
|
|
681
|
+
* const wallet = createPrivacyEthereumAdapter({ wallet: 'metamask' })
|
|
682
|
+
* await wallet.connect()
|
|
683
|
+
* await wallet.initializePrivacy()
|
|
684
|
+
* ```
|
|
685
|
+
*
|
|
686
|
+
* @example Deterministic keys from wallet
|
|
687
|
+
* ```typescript
|
|
688
|
+
* const wallet = createPrivacyEthereumAdapter({
|
|
689
|
+
* wallet: 'metamask',
|
|
690
|
+
* deriveFromWallet: true,
|
|
691
|
+
* })
|
|
692
|
+
* await wallet.connect()
|
|
693
|
+
* await wallet.initializePrivacy() // Will prompt for signature
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
export function createPrivacyEthereumAdapter(
|
|
697
|
+
config: PrivacyEthereumAdapterConfig = {}
|
|
698
|
+
): PrivacyEthereumWalletAdapter {
|
|
699
|
+
return new PrivacyEthereumWalletAdapter(config)
|
|
700
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { HexString } from '@sip-protocol/types'
|
|
9
|
+
import { ETH_RPC_ENDPOINTS } from '../../config/endpoints'
|
|
9
10
|
|
|
10
11
|
// ============================================================================
|
|
11
12
|
// EIP-1193 Provider Types
|
|
@@ -359,6 +360,7 @@ export function getDefaultRpcEndpoint(chainId: number): string {
|
|
|
359
360
|
case EthereumChainId.BASE:
|
|
360
361
|
return 'https://mainnet.base.org'
|
|
361
362
|
default:
|
|
362
|
-
|
|
363
|
+
// Configurable via ETH_LOCALNET_RPC environment variable
|
|
364
|
+
return ETH_RPC_ENDPOINTS.localnet
|
|
363
365
|
}
|
|
364
366
|
}
|