@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,855 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ethereum Privacy Adapter
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates privacy operations for Ethereum same-chain transactions.
|
|
5
|
+
* Provides a unified interface for stealth transfers, scanning, and claiming.
|
|
6
|
+
*
|
|
7
|
+
* @module chains/ethereum/privacy-adapter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { StealthMetaAddress, HexString, StealthAddress } from '@sip-protocol/types'
|
|
11
|
+
import {
|
|
12
|
+
generateEthereumStealthMetaAddress,
|
|
13
|
+
generateEthereumStealthAddress,
|
|
14
|
+
parseEthereumStealthMetaAddress,
|
|
15
|
+
encodeEthereumStealthMetaAddress,
|
|
16
|
+
deriveEthereumStealthPrivateKey,
|
|
17
|
+
checkEthereumStealthAddress,
|
|
18
|
+
type EthereumStealthMetaAddress,
|
|
19
|
+
type EthereumStealthAddress,
|
|
20
|
+
} from './stealth'
|
|
21
|
+
import {
|
|
22
|
+
commitETH,
|
|
23
|
+
commitERC20Token,
|
|
24
|
+
fromWei,
|
|
25
|
+
} from './commitment'
|
|
26
|
+
import {
|
|
27
|
+
exportViewingKey,
|
|
28
|
+
importViewingKey,
|
|
29
|
+
createSharedViewingKey,
|
|
30
|
+
type ViewingKeyPermissions,
|
|
31
|
+
} from './viewing-key'
|
|
32
|
+
import {
|
|
33
|
+
createAnnouncementMetadata,
|
|
34
|
+
encodeAnnouncementCallData,
|
|
35
|
+
announcementToStealthAddress,
|
|
36
|
+
buildAnnouncementTopics,
|
|
37
|
+
} from './announcement'
|
|
38
|
+
import {
|
|
39
|
+
type EthereumNetwork,
|
|
40
|
+
ETHEREUM_RPC_ENDPOINTS,
|
|
41
|
+
EIP5564_ANNOUNCER_ADDRESS,
|
|
42
|
+
DEFAULT_GAS_LIMITS,
|
|
43
|
+
ONE_GWEI,
|
|
44
|
+
getExplorerUrl,
|
|
45
|
+
getChainId,
|
|
46
|
+
isValidEthAddress,
|
|
47
|
+
} from './constants'
|
|
48
|
+
import type {
|
|
49
|
+
EthereumPrivacyLevel,
|
|
50
|
+
EthereumAnnouncement,
|
|
51
|
+
EthereumShieldedTransferBuild,
|
|
52
|
+
EthereumClaimParams,
|
|
53
|
+
EthereumClaimBuild,
|
|
54
|
+
EthereumGasEstimate,
|
|
55
|
+
EthereumPrivacyAdapterState,
|
|
56
|
+
EthereumScanRecipient,
|
|
57
|
+
EthereumDetectedPaymentResult,
|
|
58
|
+
EthereumViewingKeyExport,
|
|
59
|
+
EthereumViewingKeyPair,
|
|
60
|
+
EthereumPedersenCommitment,
|
|
61
|
+
} from './types'
|
|
62
|
+
|
|
63
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Configuration for EthereumPrivacyAdapter
|
|
67
|
+
*/
|
|
68
|
+
export interface EthereumPrivacyAdapterConfig {
|
|
69
|
+
/**
|
|
70
|
+
* Ethereum RPC URL
|
|
71
|
+
*/
|
|
72
|
+
rpcUrl?: string
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Network type
|
|
76
|
+
* @default 'mainnet'
|
|
77
|
+
*/
|
|
78
|
+
network?: EthereumNetwork
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Default privacy level
|
|
82
|
+
* @default 'shielded'
|
|
83
|
+
*/
|
|
84
|
+
defaultPrivacyLevel?: EthereumPrivacyLevel
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Custom announcer contract address
|
|
88
|
+
*/
|
|
89
|
+
announcerAddress?: HexString
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Enable amount hiding with Pedersen commitments
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
hideAmounts?: boolean
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parameters for shielded ETH transfer
|
|
100
|
+
*/
|
|
101
|
+
export interface EthereumShieldedTransferParams {
|
|
102
|
+
/**
|
|
103
|
+
* Recipient's stealth meta-address
|
|
104
|
+
*/
|
|
105
|
+
recipient: StealthMetaAddress | string
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Amount in wei
|
|
109
|
+
*/
|
|
110
|
+
amount: bigint
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Privacy level for this transaction
|
|
114
|
+
*/
|
|
115
|
+
privacyLevel?: EthereumPrivacyLevel
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Optional memo/reference
|
|
119
|
+
*/
|
|
120
|
+
memo?: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Parameters for shielded ERC-20 transfer
|
|
125
|
+
*/
|
|
126
|
+
export interface EthereumShieldedTokenTransferParams extends EthereumShieldedTransferParams {
|
|
127
|
+
/**
|
|
128
|
+
* ERC-20 token contract address
|
|
129
|
+
*/
|
|
130
|
+
tokenContract: HexString
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Token decimals (for display purposes)
|
|
134
|
+
*/
|
|
135
|
+
decimals?: number
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Built transaction for signing
|
|
140
|
+
*/
|
|
141
|
+
export interface EthereumBuiltTransaction {
|
|
142
|
+
/**
|
|
143
|
+
* Target address
|
|
144
|
+
*/
|
|
145
|
+
to: HexString
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* ETH value in wei
|
|
149
|
+
*/
|
|
150
|
+
value: bigint
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Transaction data (for contract calls)
|
|
154
|
+
*/
|
|
155
|
+
data?: HexString
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Suggested gas limit
|
|
159
|
+
*/
|
|
160
|
+
gasLimit: bigint
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Chain ID
|
|
164
|
+
*/
|
|
165
|
+
chainId: number
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─── EthereumPrivacyAdapter Class ─────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Ethereum Privacy Adapter
|
|
172
|
+
*
|
|
173
|
+
* Provides a unified interface for privacy operations on Ethereum:
|
|
174
|
+
* - Shielded transfers to stealth addresses
|
|
175
|
+
* - Payment scanning and detection
|
|
176
|
+
* - Claiming detected payments
|
|
177
|
+
* - Meta-address and keypair generation
|
|
178
|
+
*
|
|
179
|
+
* @example Basic usage
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const adapter = new EthereumPrivacyAdapter({
|
|
182
|
+
* rpcUrl: 'https://eth.llamarpc.com',
|
|
183
|
+
* network: 'mainnet',
|
|
184
|
+
* })
|
|
185
|
+
*
|
|
186
|
+
* // Generate meta-address for recipient
|
|
187
|
+
* const { metaAddress, viewingPrivateKey, spendingPrivateKey } =
|
|
188
|
+
* adapter.generateMetaAddress('Primary Wallet')
|
|
189
|
+
*
|
|
190
|
+
* // Build shielded transfer
|
|
191
|
+
* const build = adapter.buildShieldedTransfer({
|
|
192
|
+
* recipient: recipientMetaAddress,
|
|
193
|
+
* amount: toWei(1), // 1 ETH
|
|
194
|
+
* })
|
|
195
|
+
*
|
|
196
|
+
* // Sign and submit transactions externally
|
|
197
|
+
* // 1. Send ETH to stealth address
|
|
198
|
+
* // 2. Announce the payment
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @example Scanning and claiming
|
|
202
|
+
* ```typescript
|
|
203
|
+
* // Add recipient for scanning
|
|
204
|
+
* adapter.addScanRecipient({
|
|
205
|
+
* viewingPrivateKey,
|
|
206
|
+
* spendingPublicKey: metaAddress.spendingKey,
|
|
207
|
+
* label: 'Main Wallet',
|
|
208
|
+
* })
|
|
209
|
+
*
|
|
210
|
+
* // Scan announcements
|
|
211
|
+
* const payments = await adapter.scanAnnouncements(announcements)
|
|
212
|
+
*
|
|
213
|
+
* // Claim a payment
|
|
214
|
+
* const claimBuild = adapter.buildClaimTransaction({
|
|
215
|
+
* payment: payments[0],
|
|
216
|
+
* viewingPrivateKey,
|
|
217
|
+
* spendingPrivateKey,
|
|
218
|
+
* destinationAddress: '0x...',
|
|
219
|
+
* })
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export class EthereumPrivacyAdapter {
|
|
223
|
+
private rpcUrl: string
|
|
224
|
+
private network: EthereumNetwork
|
|
225
|
+
private chainId: number
|
|
226
|
+
private defaultPrivacyLevel: EthereumPrivacyLevel
|
|
227
|
+
private announcerAddress: HexString
|
|
228
|
+
private hideAmounts: boolean
|
|
229
|
+
private scanRecipients: Map<string, EthereumScanRecipient> = new Map()
|
|
230
|
+
private lastScannedBlock?: number
|
|
231
|
+
|
|
232
|
+
constructor(config: EthereumPrivacyAdapterConfig = {}) {
|
|
233
|
+
this.network = config.network ?? 'mainnet'
|
|
234
|
+
this.rpcUrl = config.rpcUrl ?? ETHEREUM_RPC_ENDPOINTS[this.network]
|
|
235
|
+
this.chainId = getChainId(this.network)
|
|
236
|
+
this.defaultPrivacyLevel = config.defaultPrivacyLevel ?? 'shielded'
|
|
237
|
+
this.announcerAddress = config.announcerAddress ?? (EIP5564_ANNOUNCER_ADDRESS as HexString)
|
|
238
|
+
this.hideAmounts = config.hideAmounts ?? true
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── Meta-Address Generation ────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Generate a new stealth meta-address
|
|
245
|
+
*
|
|
246
|
+
* Creates a new keypair for receiving private payments.
|
|
247
|
+
* The meta-address can be shared publicly; only the private keys
|
|
248
|
+
* enable scanning and claiming.
|
|
249
|
+
*
|
|
250
|
+
* @param label - Optional label for the address
|
|
251
|
+
* @returns Meta-address and private keys
|
|
252
|
+
*/
|
|
253
|
+
generateMetaAddress(label?: string): {
|
|
254
|
+
metaAddress: EthereumStealthMetaAddress
|
|
255
|
+
encoded: string
|
|
256
|
+
viewingPrivateKey: HexString
|
|
257
|
+
spendingPrivateKey: HexString
|
|
258
|
+
} {
|
|
259
|
+
const result = generateEthereumStealthMetaAddress(label)
|
|
260
|
+
return {
|
|
261
|
+
metaAddress: result.metaAddress,
|
|
262
|
+
encoded: result.encoded,
|
|
263
|
+
viewingPrivateKey: result.viewingPrivateKey,
|
|
264
|
+
spendingPrivateKey: result.spendingPrivateKey,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Parse an encoded meta-address string
|
|
270
|
+
*
|
|
271
|
+
* @param encoded - Encoded meta-address (st:eth:0x...)
|
|
272
|
+
* @returns Decoded meta-address
|
|
273
|
+
*/
|
|
274
|
+
parseMetaAddress(encoded: string): EthereumStealthMetaAddress {
|
|
275
|
+
return parseEthereumStealthMetaAddress(encoded)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Encode a meta-address to string format
|
|
280
|
+
*
|
|
281
|
+
* @param metaAddress - Meta-address to encode
|
|
282
|
+
* @returns Encoded string (st:eth:0x...)
|
|
283
|
+
*/
|
|
284
|
+
encodeMetaAddress(metaAddress: StealthMetaAddress): string {
|
|
285
|
+
return encodeEthereumStealthMetaAddress(metaAddress)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ─── Viewing Key Management ─────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Export a viewing key for sharing (compliance)
|
|
292
|
+
*
|
|
293
|
+
* @param viewingKeyPair - Viewing keypair to export
|
|
294
|
+
* @param expiresAt - Optional expiration date
|
|
295
|
+
* @returns Exportable viewing key data
|
|
296
|
+
*/
|
|
297
|
+
exportViewingKey(
|
|
298
|
+
viewingKeyPair: EthereumViewingKeyPair,
|
|
299
|
+
expiresAt?: Date
|
|
300
|
+
): EthereumViewingKeyExport {
|
|
301
|
+
return exportViewingKey(viewingKeyPair, this.network, expiresAt)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Import a viewing key from export format
|
|
306
|
+
*
|
|
307
|
+
* @param exported - Exported viewing key data (JSON string or object)
|
|
308
|
+
* @returns Parsed viewing key export
|
|
309
|
+
*/
|
|
310
|
+
importViewingKey(exported: string | EthereumViewingKeyExport): EthereumViewingKeyExport {
|
|
311
|
+
return importViewingKey(exported)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Create a shared viewing key with specific permissions
|
|
316
|
+
*
|
|
317
|
+
* @param viewingKeyPair - The full viewing keypair
|
|
318
|
+
* @param permissions - Permissions to grant
|
|
319
|
+
* @param expiresAt - Optional expiration
|
|
320
|
+
* @returns Shared viewing key for auditor
|
|
321
|
+
*/
|
|
322
|
+
createSharedViewingKey(
|
|
323
|
+
viewingKeyPair: EthereumViewingKeyPair,
|
|
324
|
+
permissions: ViewingKeyPermissions,
|
|
325
|
+
expiresAt?: Date
|
|
326
|
+
) {
|
|
327
|
+
return createSharedViewingKey(viewingKeyPair, permissions, expiresAt)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ─── Stealth Address Resolution ─────────────────────────────────────────────
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Resolve a meta-address to a one-time stealth address
|
|
334
|
+
*
|
|
335
|
+
* Generates a fresh stealth address for the recipient.
|
|
336
|
+
* Each call produces a different, unlinkable address.
|
|
337
|
+
*
|
|
338
|
+
* @param recipient - Recipient's meta-address
|
|
339
|
+
* @returns Stealth address details
|
|
340
|
+
*/
|
|
341
|
+
resolveStealthAddress(recipient: StealthMetaAddress | string): {
|
|
342
|
+
stealthAddress: EthereumStealthAddress
|
|
343
|
+
ethAddress: HexString
|
|
344
|
+
sharedSecret: HexString
|
|
345
|
+
} {
|
|
346
|
+
const metaAddress = typeof recipient === 'string'
|
|
347
|
+
? parseEthereumStealthMetaAddress(recipient)
|
|
348
|
+
: recipient
|
|
349
|
+
|
|
350
|
+
const { stealthAddress, sharedSecret } = generateEthereumStealthAddress(metaAddress)
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
stealthAddress,
|
|
354
|
+
ethAddress: stealthAddress.ethAddress,
|
|
355
|
+
sharedSecret,
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Check if a stealth address belongs to a recipient
|
|
361
|
+
*
|
|
362
|
+
* @param stealthAddress - Stealth address object
|
|
363
|
+
* @param spendingPrivateKey - Spending private key (hex)
|
|
364
|
+
* @param viewingPrivateKey - Viewing private key (hex)
|
|
365
|
+
* @returns True if the address belongs to the recipient
|
|
366
|
+
*/
|
|
367
|
+
checkStealthAddress(
|
|
368
|
+
stealthAddress: StealthAddress,
|
|
369
|
+
spendingPrivateKey: HexString,
|
|
370
|
+
viewingPrivateKey: HexString
|
|
371
|
+
): boolean {
|
|
372
|
+
return checkEthereumStealthAddress(
|
|
373
|
+
stealthAddress,
|
|
374
|
+
spendingPrivateKey,
|
|
375
|
+
viewingPrivateKey
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ─── Shielded Transfers ─────────────────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Build a shielded ETH transfer
|
|
383
|
+
*
|
|
384
|
+
* Creates transaction data for a private ETH transfer.
|
|
385
|
+
* Returns two transactions:
|
|
386
|
+
* 1. ETH transfer to stealth address
|
|
387
|
+
* 2. Announcement to EIP-5564 contract
|
|
388
|
+
*
|
|
389
|
+
* @param params - Transfer parameters
|
|
390
|
+
* @returns Built transfer ready for signing
|
|
391
|
+
*/
|
|
392
|
+
buildShieldedTransfer(params: EthereumShieldedTransferParams): EthereumShieldedTransferBuild {
|
|
393
|
+
const privacyLevel = params.privacyLevel ?? this.defaultPrivacyLevel
|
|
394
|
+
|
|
395
|
+
// For transparent level, throw - use regular transfer instead
|
|
396
|
+
if (privacyLevel === 'transparent') {
|
|
397
|
+
throw new Error('Use regular ETH transfer for transparent privacy level')
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const metaAddress = typeof params.recipient === 'string'
|
|
401
|
+
? parseEthereumStealthMetaAddress(params.recipient)
|
|
402
|
+
: params.recipient
|
|
403
|
+
|
|
404
|
+
// Generate stealth address
|
|
405
|
+
const { stealthAddress, sharedSecret } = generateEthereumStealthAddress(metaAddress)
|
|
406
|
+
|
|
407
|
+
// Create amount commitment if hiding amounts
|
|
408
|
+
// Note: privacyLevel is guaranteed to be 'shielded' or 'compliant' here (transparent throws above)
|
|
409
|
+
let amountCommitment: EthereumPedersenCommitment | undefined
|
|
410
|
+
if (this.hideAmounts) {
|
|
411
|
+
amountCommitment = commitETH(params.amount)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Create metadata for announcement
|
|
415
|
+
const metadata = this.hideAmounts && amountCommitment
|
|
416
|
+
? createAnnouncementMetadata({
|
|
417
|
+
amountCommitment: amountCommitment.commitment,
|
|
418
|
+
})
|
|
419
|
+
: undefined
|
|
420
|
+
|
|
421
|
+
// Build announcement call data
|
|
422
|
+
const announcementData = encodeAnnouncementCallData(
|
|
423
|
+
1, // schemeId for secp256k1
|
|
424
|
+
stealthAddress.ethAddress,
|
|
425
|
+
stealthAddress.ephemeralPublicKey,
|
|
426
|
+
metadata
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
// Estimate gas
|
|
430
|
+
const transferGas = DEFAULT_GAS_LIMITS.ethTransfer
|
|
431
|
+
const announcementGas = DEFAULT_GAS_LIMITS.announcement
|
|
432
|
+
const totalGas = transferGas + announcementGas
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
stealthAddress,
|
|
436
|
+
stealthEthAddress: stealthAddress.ethAddress,
|
|
437
|
+
ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
|
|
438
|
+
viewTag: stealthAddress.viewTag,
|
|
439
|
+
sharedSecret,
|
|
440
|
+
amountCommitment: amountCommitment?.commitment,
|
|
441
|
+
blindingFactor: amountCommitment?.blinding,
|
|
442
|
+
transferTx: {
|
|
443
|
+
to: stealthAddress.ethAddress,
|
|
444
|
+
value: params.amount,
|
|
445
|
+
},
|
|
446
|
+
announcementTx: {
|
|
447
|
+
to: this.announcerAddress,
|
|
448
|
+
value: 0n,
|
|
449
|
+
data: announcementData,
|
|
450
|
+
},
|
|
451
|
+
estimatedGas: totalGas,
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Build a shielded ERC-20 token transfer
|
|
457
|
+
*
|
|
458
|
+
* Creates transaction data for a private token transfer.
|
|
459
|
+
* Returns multiple transactions:
|
|
460
|
+
* 1. Token transfer to stealth address
|
|
461
|
+
* 2. Announcement to EIP-5564 contract
|
|
462
|
+
*
|
|
463
|
+
* @param params - Transfer parameters
|
|
464
|
+
* @returns Built transfer ready for signing
|
|
465
|
+
*/
|
|
466
|
+
buildShieldedTokenTransfer(
|
|
467
|
+
params: EthereumShieldedTokenTransferParams
|
|
468
|
+
): EthereumShieldedTransferBuild & { tokenTransferData: HexString } {
|
|
469
|
+
const privacyLevel = params.privacyLevel ?? this.defaultPrivacyLevel
|
|
470
|
+
|
|
471
|
+
if (privacyLevel === 'transparent') {
|
|
472
|
+
throw new Error('Use regular ERC-20 transfer for transparent privacy level')
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!isValidEthAddress(params.tokenContract)) {
|
|
476
|
+
throw new Error(`Invalid token contract: ${params.tokenContract}`)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const metaAddress = typeof params.recipient === 'string'
|
|
480
|
+
? parseEthereumStealthMetaAddress(params.recipient)
|
|
481
|
+
: params.recipient
|
|
482
|
+
|
|
483
|
+
// Generate stealth address
|
|
484
|
+
const { stealthAddress, sharedSecret } = generateEthereumStealthAddress(metaAddress)
|
|
485
|
+
|
|
486
|
+
// Create amount commitment
|
|
487
|
+
const decimals = params.decimals ?? 18
|
|
488
|
+
const amountCommitment = this.hideAmounts
|
|
489
|
+
? commitERC20Token(params.amount, params.tokenContract, decimals)
|
|
490
|
+
: undefined
|
|
491
|
+
|
|
492
|
+
// Create metadata for announcement
|
|
493
|
+
const metadata = createAnnouncementMetadata({
|
|
494
|
+
tokenAddress: params.tokenContract,
|
|
495
|
+
amountCommitment: amountCommitment?.commitment,
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
// Build ERC-20 transfer data
|
|
499
|
+
// transfer(address,uint256) selector: 0xa9059cbb
|
|
500
|
+
const tokenTransferData = this.encodeERC20Transfer(
|
|
501
|
+
stealthAddress.ethAddress,
|
|
502
|
+
params.amount
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
// Build announcement call data
|
|
506
|
+
const announcementData = encodeAnnouncementCallData(
|
|
507
|
+
1,
|
|
508
|
+
stealthAddress.ethAddress,
|
|
509
|
+
stealthAddress.ephemeralPublicKey,
|
|
510
|
+
metadata
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
// Estimate gas
|
|
514
|
+
const transferGas = DEFAULT_GAS_LIMITS.erc20Transfer
|
|
515
|
+
const announcementGas = DEFAULT_GAS_LIMITS.announcement
|
|
516
|
+
const totalGas = transferGas + announcementGas
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
stealthAddress,
|
|
520
|
+
stealthEthAddress: stealthAddress.ethAddress,
|
|
521
|
+
ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
|
|
522
|
+
viewTag: stealthAddress.viewTag,
|
|
523
|
+
sharedSecret,
|
|
524
|
+
amountCommitment: amountCommitment?.commitment,
|
|
525
|
+
blindingFactor: amountCommitment?.blinding,
|
|
526
|
+
transferTx: {
|
|
527
|
+
to: params.tokenContract,
|
|
528
|
+
value: 0n,
|
|
529
|
+
data: tokenTransferData,
|
|
530
|
+
},
|
|
531
|
+
announcementTx: {
|
|
532
|
+
to: this.announcerAddress,
|
|
533
|
+
value: 0n,
|
|
534
|
+
data: announcementData,
|
|
535
|
+
},
|
|
536
|
+
estimatedGas: totalGas,
|
|
537
|
+
tokenTransferData,
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ─── Payment Scanning ───────────────────────────────────────────────────────
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Add a recipient for payment scanning
|
|
545
|
+
*
|
|
546
|
+
* @param recipient - Scan recipient with viewing key
|
|
547
|
+
*/
|
|
548
|
+
addScanRecipient(recipient: EthereumScanRecipient): void {
|
|
549
|
+
const key = recipient.viewingPrivateKey.toLowerCase()
|
|
550
|
+
this.scanRecipients.set(key, recipient)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Remove a scan recipient
|
|
555
|
+
*
|
|
556
|
+
* @param viewingPrivateKey - The viewing key to remove
|
|
557
|
+
*/
|
|
558
|
+
removeScanRecipient(viewingPrivateKey: HexString): void {
|
|
559
|
+
this.scanRecipients.delete(viewingPrivateKey.toLowerCase())
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Get all scan recipients
|
|
564
|
+
*/
|
|
565
|
+
getScanRecipients(): EthereumScanRecipient[] {
|
|
566
|
+
return Array.from(this.scanRecipients.values())
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Scan announcements for incoming payments
|
|
571
|
+
*
|
|
572
|
+
* @param announcements - Announcements to scan
|
|
573
|
+
* @returns Detected payments with recipient info
|
|
574
|
+
*/
|
|
575
|
+
scanAnnouncements(
|
|
576
|
+
announcements: EthereumAnnouncement[]
|
|
577
|
+
): EthereumDetectedPaymentResult[] {
|
|
578
|
+
const results: EthereumDetectedPaymentResult[] = []
|
|
579
|
+
|
|
580
|
+
for (const announcement of announcements) {
|
|
581
|
+
const stealthAddress = announcementToStealthAddress(announcement)
|
|
582
|
+
|
|
583
|
+
// Check each recipient
|
|
584
|
+
for (const recipient of this.scanRecipients.values()) {
|
|
585
|
+
const isOwner = checkEthereumStealthAddress(
|
|
586
|
+
stealthAddress,
|
|
587
|
+
recipient.spendingPublicKey, // Note: need spending PRIVATE key for full check
|
|
588
|
+
recipient.viewingPrivateKey
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
if (isOwner) {
|
|
592
|
+
// Derive stealth private key for claiming
|
|
593
|
+
const recovery = deriveEthereumStealthPrivateKey(
|
|
594
|
+
stealthAddress,
|
|
595
|
+
recipient.spendingPublicKey, // This should be spending PRIVATE key
|
|
596
|
+
recipient.viewingPrivateKey
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
results.push({
|
|
600
|
+
payment: {
|
|
601
|
+
stealthAddress,
|
|
602
|
+
stealthEthAddress: announcement.stealthAddress,
|
|
603
|
+
txHash: announcement.txHash!,
|
|
604
|
+
blockNumber: announcement.blockNumber!,
|
|
605
|
+
logIndex: announcement.logIndex,
|
|
606
|
+
timestamp: announcement.timestamp,
|
|
607
|
+
},
|
|
608
|
+
recipient,
|
|
609
|
+
stealthPrivateKey: recovery.privateKey,
|
|
610
|
+
})
|
|
611
|
+
break // Found owner, no need to check other recipients
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return results
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Get topics for filtering announcement logs
|
|
621
|
+
*
|
|
622
|
+
* @param options - Optional filters
|
|
623
|
+
* @returns Topics array for eth_getLogs
|
|
624
|
+
*/
|
|
625
|
+
getAnnouncementTopics(options?: {
|
|
626
|
+
schemeId?: number
|
|
627
|
+
stealthAddress?: HexString
|
|
628
|
+
caller?: HexString
|
|
629
|
+
}): (HexString | null)[] {
|
|
630
|
+
return buildAnnouncementTopics(options)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// ─── Claiming ───────────────────────────────────────────────────────────────
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Build a claim transaction
|
|
637
|
+
*
|
|
638
|
+
* Creates transaction data to claim funds from a stealth address.
|
|
639
|
+
*
|
|
640
|
+
* @param params - Claim parameters
|
|
641
|
+
* @returns Built claim transaction
|
|
642
|
+
*/
|
|
643
|
+
buildClaimTransaction(params: EthereumClaimParams): EthereumClaimBuild {
|
|
644
|
+
// Derive stealth private key
|
|
645
|
+
const recovery = deriveEthereumStealthPrivateKey(
|
|
646
|
+
params.stealthAddress,
|
|
647
|
+
params.spendingPrivateKey,
|
|
648
|
+
params.viewingPrivateKey
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
// Build transaction
|
|
652
|
+
const amount = params.amount ?? 0n // Full balance if not specified
|
|
653
|
+
|
|
654
|
+
let tx: { to: HexString; value: bigint; data?: HexString }
|
|
655
|
+
|
|
656
|
+
if (params.tokenContract) {
|
|
657
|
+
// ERC-20 claim
|
|
658
|
+
const transferData = this.encodeERC20Transfer(
|
|
659
|
+
params.destinationAddress,
|
|
660
|
+
amount
|
|
661
|
+
)
|
|
662
|
+
tx = {
|
|
663
|
+
to: params.tokenContract,
|
|
664
|
+
value: 0n,
|
|
665
|
+
data: transferData,
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
// Native ETH claim
|
|
669
|
+
tx = {
|
|
670
|
+
to: params.destinationAddress,
|
|
671
|
+
value: amount,
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
stealthEthAddress: recovery.ethAddress,
|
|
677
|
+
stealthPrivateKey: recovery.privateKey,
|
|
678
|
+
destinationAddress: params.destinationAddress,
|
|
679
|
+
amount,
|
|
680
|
+
tx,
|
|
681
|
+
estimatedGas: params.tokenContract
|
|
682
|
+
? DEFAULT_GAS_LIMITS.erc20Transfer
|
|
683
|
+
: DEFAULT_GAS_LIMITS.ethTransfer,
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ─── Gas Estimation ─────────────────────────────────────────────────────────
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Estimate gas for a shielded transfer
|
|
691
|
+
*
|
|
692
|
+
* @param isTokenTransfer - Whether this is a token transfer
|
|
693
|
+
* @returns Gas estimate
|
|
694
|
+
*/
|
|
695
|
+
estimateTransferGas(isTokenTransfer: boolean = false): EthereumGasEstimate {
|
|
696
|
+
const transferGas = isTokenTransfer
|
|
697
|
+
? DEFAULT_GAS_LIMITS.erc20Transfer
|
|
698
|
+
: DEFAULT_GAS_LIMITS.ethTransfer
|
|
699
|
+
const announcementGas = DEFAULT_GAS_LIMITS.announcement
|
|
700
|
+
|
|
701
|
+
const totalGas = transferGas + announcementGas
|
|
702
|
+
const gasPrice = 30n * ONE_GWEI // Assume 30 gwei
|
|
703
|
+
|
|
704
|
+
const estimatedCost = totalGas * gasPrice
|
|
705
|
+
|
|
706
|
+
return {
|
|
707
|
+
gasLimit: totalGas,
|
|
708
|
+
gasPrice,
|
|
709
|
+
estimatedCost,
|
|
710
|
+
estimatedCostEth: fromWei(estimatedCost),
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Estimate gas for claiming a payment
|
|
716
|
+
*
|
|
717
|
+
* @param isTokenClaim - Whether claiming tokens
|
|
718
|
+
* @returns Gas estimate
|
|
719
|
+
*/
|
|
720
|
+
estimateClaimGas(isTokenClaim: boolean = false): EthereumGasEstimate {
|
|
721
|
+
const claimGas = isTokenClaim
|
|
722
|
+
? DEFAULT_GAS_LIMITS.erc20Transfer
|
|
723
|
+
: DEFAULT_GAS_LIMITS.ethTransfer
|
|
724
|
+
|
|
725
|
+
const gasPrice = 30n * ONE_GWEI
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
gasLimit: claimGas,
|
|
729
|
+
gasPrice,
|
|
730
|
+
estimatedCost: claimGas * gasPrice,
|
|
731
|
+
estimatedCostEth: fromWei(claimGas * gasPrice),
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// ─── Utility Methods ────────────────────────────────────────────────────────
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Get adapter state
|
|
739
|
+
*/
|
|
740
|
+
getState(): EthereumPrivacyAdapterState {
|
|
741
|
+
return {
|
|
742
|
+
network: this.network,
|
|
743
|
+
rpcUrl: this.rpcUrl,
|
|
744
|
+
chainId: this.chainId,
|
|
745
|
+
defaultPrivacyLevel: this.defaultPrivacyLevel,
|
|
746
|
+
scanRecipientCount: this.scanRecipients.size,
|
|
747
|
+
lastScannedBlock: this.lastScannedBlock,
|
|
748
|
+
isConnected: true,
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Get RPC URL
|
|
754
|
+
*/
|
|
755
|
+
getRpcUrl(): string {
|
|
756
|
+
return this.rpcUrl
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Get network
|
|
761
|
+
*/
|
|
762
|
+
getNetwork(): EthereumNetwork {
|
|
763
|
+
return this.network
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Get chain ID
|
|
768
|
+
*/
|
|
769
|
+
getChainId(): number {
|
|
770
|
+
return this.chainId
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Get transaction explorer URL
|
|
775
|
+
*
|
|
776
|
+
* @param txHash - Transaction hash
|
|
777
|
+
* @returns Explorer URL
|
|
778
|
+
*/
|
|
779
|
+
getTransactionExplorerUrl(txHash: HexString): string {
|
|
780
|
+
return getExplorerUrl(txHash, this.network)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Encode ERC-20 transfer data
|
|
785
|
+
*
|
|
786
|
+
* @param to - Recipient address
|
|
787
|
+
* @param amount - Amount in token units
|
|
788
|
+
* @returns Encoded call data
|
|
789
|
+
*/
|
|
790
|
+
private encodeERC20Transfer(to: HexString, amount: bigint): HexString {
|
|
791
|
+
// transfer(address,uint256) selector
|
|
792
|
+
const selector = '0xa9059cbb'
|
|
793
|
+
const toParam = to.slice(2).padStart(64, '0')
|
|
794
|
+
const amountParam = amount.toString(16).padStart(64, '0')
|
|
795
|
+
|
|
796
|
+
return `${selector}${toParam}${amountParam}` as HexString
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Clean up adapter resources
|
|
801
|
+
*/
|
|
802
|
+
dispose(): void {
|
|
803
|
+
this.scanRecipients.clear()
|
|
804
|
+
this.lastScannedBlock = undefined
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// ─── Factory Functions ────────────────────────────────────────────────────────
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Create an Ethereum privacy adapter
|
|
812
|
+
*
|
|
813
|
+
* @param config - Adapter configuration
|
|
814
|
+
* @returns Configured adapter
|
|
815
|
+
*/
|
|
816
|
+
export function createEthereumPrivacyAdapter(
|
|
817
|
+
config?: EthereumPrivacyAdapterConfig
|
|
818
|
+
): EthereumPrivacyAdapter {
|
|
819
|
+
return new EthereumPrivacyAdapter(config)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Create a mainnet Ethereum privacy adapter
|
|
824
|
+
*/
|
|
825
|
+
export function createMainnetEthereumPrivacyAdapter(): EthereumPrivacyAdapter {
|
|
826
|
+
return new EthereumPrivacyAdapter({ network: 'mainnet' })
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Create a Sepolia testnet privacy adapter
|
|
831
|
+
*/
|
|
832
|
+
export function createSepoliaEthereumPrivacyAdapter(): EthereumPrivacyAdapter {
|
|
833
|
+
return new EthereumPrivacyAdapter({ network: 'sepolia' })
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Create an Arbitrum privacy adapter
|
|
838
|
+
*/
|
|
839
|
+
export function createArbitrumPrivacyAdapter(): EthereumPrivacyAdapter {
|
|
840
|
+
return new EthereumPrivacyAdapter({ network: 'arbitrum' })
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Create an Optimism privacy adapter
|
|
845
|
+
*/
|
|
846
|
+
export function createOptimismPrivacyAdapter(): EthereumPrivacyAdapter {
|
|
847
|
+
return new EthereumPrivacyAdapter({ network: 'optimism' })
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Create a Base privacy adapter
|
|
852
|
+
*/
|
|
853
|
+
export function createBasePrivacyAdapter(): EthereumPrivacyAdapter {
|
|
854
|
+
return new EthereumPrivacyAdapter({ network: 'base' })
|
|
855
|
+
}
|