@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,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicBlock Privacy Backend
|
|
3
|
+
*
|
|
4
|
+
* Integrates MagicBlock's TEE-based Private Ephemeral Rollup (PER) as a
|
|
5
|
+
* privacy backend for SIP Protocol.
|
|
6
|
+
*
|
|
7
|
+
* MagicBlock uses Intel TDX (Trust Domain Extension) for hardware-based privacy.
|
|
8
|
+
* SIP adds viewing keys on top for compliance support.
|
|
9
|
+
*
|
|
10
|
+
* @see https://magicblock.gg
|
|
11
|
+
* @see https://docs.magicblock.gg
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { MagicBlockBackend, PrivacyBackendRegistry } from '@sip-protocol/sdk'
|
|
16
|
+
*
|
|
17
|
+
* const backend = new MagicBlockBackend({
|
|
18
|
+
* network: 'devnet',
|
|
19
|
+
* })
|
|
20
|
+
* const registry = new PrivacyBackendRegistry()
|
|
21
|
+
* registry.register(backend)
|
|
22
|
+
*
|
|
23
|
+
* // Execute private transfer via TEE
|
|
24
|
+
* const result = await backend.execute({
|
|
25
|
+
* chain: 'solana',
|
|
26
|
+
* sender: 'sender-pubkey',
|
|
27
|
+
* recipient: 'recipient-pubkey',
|
|
28
|
+
* mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
|
|
29
|
+
* amount: 1000000n, // 1 USDC
|
|
30
|
+
* decimals: 6,
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
ConnectionMagicRouter,
|
|
37
|
+
DELEGATION_PROGRAM_ID,
|
|
38
|
+
MAGIC_PROGRAM_ID,
|
|
39
|
+
delegateSpl,
|
|
40
|
+
withdrawSplIx,
|
|
41
|
+
deriveEphemeralAta,
|
|
42
|
+
} from '@magicblock-labs/ephemeral-rollups-sdk'
|
|
43
|
+
import {
|
|
44
|
+
Connection,
|
|
45
|
+
PublicKey,
|
|
46
|
+
Transaction,
|
|
47
|
+
TransactionInstruction,
|
|
48
|
+
Keypair,
|
|
49
|
+
} from '@solana/web3.js'
|
|
50
|
+
import type { ChainType, ViewingKey, HexString } from '@sip-protocol/types'
|
|
51
|
+
import type {
|
|
52
|
+
PrivacyBackend,
|
|
53
|
+
BackendType,
|
|
54
|
+
BackendCapabilities,
|
|
55
|
+
TransferParams,
|
|
56
|
+
TransactionResult,
|
|
57
|
+
AvailabilityResult,
|
|
58
|
+
BackendParams,
|
|
59
|
+
} from './interface'
|
|
60
|
+
import { isTransferParams } from './interface'
|
|
61
|
+
import { generateViewingKey, encryptForViewing } from '../privacy'
|
|
62
|
+
import { bytesToHex } from '@noble/hashes/utils'
|
|
63
|
+
import { createPrivacyLogger } from '../privacy-logger'
|
|
64
|
+
|
|
65
|
+
/** Privacy-aware logger for MagicBlock backend */
|
|
66
|
+
const magicBlockLogger = createPrivacyLogger('MagicBlock')
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* MagicBlock network type
|
|
70
|
+
*/
|
|
71
|
+
export type MagicBlockNetwork = 'devnet' | 'mainnet-beta'
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* MagicBlock region endpoints
|
|
75
|
+
*/
|
|
76
|
+
export const MAGICBLOCK_ENDPOINTS: Record<string, string> = {
|
|
77
|
+
'devnet-us': 'https://devnet-us.magicblock.app',
|
|
78
|
+
'devnet-eu': 'https://devnet-eu.magicblock.app',
|
|
79
|
+
'devnet-asia': 'https://devnet-as.magicblock.app',
|
|
80
|
+
'mainnet-us': 'https://mainnet-us.magicblock.app',
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Default Solana RPC endpoints by network
|
|
85
|
+
*/
|
|
86
|
+
const SOLANA_RPC_ENDPOINTS: Record<MagicBlockNetwork, string> = {
|
|
87
|
+
devnet: 'https://api.devnet.solana.com',
|
|
88
|
+
'mainnet-beta': 'https://api.mainnet-beta.solana.com',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* MagicBlock backend configuration
|
|
93
|
+
*/
|
|
94
|
+
export interface MagicBlockBackendConfig {
|
|
95
|
+
/** Network (devnet or mainnet-beta) */
|
|
96
|
+
network?: MagicBlockNetwork
|
|
97
|
+
/** MagicBlock endpoint region (us, eu, asia) */
|
|
98
|
+
region?: 'us' | 'eu' | 'asia'
|
|
99
|
+
/** Custom Solana RPC URL */
|
|
100
|
+
rpcUrl?: string
|
|
101
|
+
/** Enable debug logging */
|
|
102
|
+
debug?: boolean
|
|
103
|
+
/** Wallet keypair for signing (optional, can be set later) */
|
|
104
|
+
wallet?: Keypair
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* MagicBlock backend capabilities
|
|
109
|
+
* TEE provides hardware-based privacy but different trust model than ZK
|
|
110
|
+
*/
|
|
111
|
+
const MAGICBLOCK_CAPABILITIES: BackendCapabilities = {
|
|
112
|
+
hiddenAmount: true,
|
|
113
|
+
hiddenSender: true,
|
|
114
|
+
hiddenRecipient: true,
|
|
115
|
+
hiddenCompute: true, // TEE hides computation
|
|
116
|
+
complianceSupport: true, // SIP adds viewing keys
|
|
117
|
+
anonymitySet: undefined,
|
|
118
|
+
setupRequired: true, // Requires delegation setup
|
|
119
|
+
latencyEstimate: 'fast', // Near real-time in TEE
|
|
120
|
+
supportedTokens: 'spl',
|
|
121
|
+
minAmount: 1n,
|
|
122
|
+
maxAmount: undefined,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* MagicBlock Privacy Backend
|
|
127
|
+
*
|
|
128
|
+
* Wraps MagicBlock's ephemeral rollups SDK to provide a unified PrivacyBackend interface.
|
|
129
|
+
* Adds SIP's viewing key support for compliance.
|
|
130
|
+
*
|
|
131
|
+
* Trust model: Hardware-based (Intel TDX TEE)
|
|
132
|
+
*/
|
|
133
|
+
export class MagicBlockBackend implements PrivacyBackend {
|
|
134
|
+
readonly name = 'magicblock'
|
|
135
|
+
readonly type: BackendType = 'both' // Supports both transfer and compute
|
|
136
|
+
readonly chains: ChainType[] = ['solana']
|
|
137
|
+
|
|
138
|
+
private connection: Connection
|
|
139
|
+
private magicRouter: ConnectionMagicRouter
|
|
140
|
+
private config: Required<Omit<MagicBlockBackendConfig, 'wallet'>> & { wallet?: Keypair }
|
|
141
|
+
private wallet?: Keypair
|
|
142
|
+
|
|
143
|
+
constructor(config: MagicBlockBackendConfig = {}) {
|
|
144
|
+
// Validate network parameter if provided
|
|
145
|
+
if (config.network !== undefined) {
|
|
146
|
+
const validNetworks: MagicBlockNetwork[] = ['devnet', 'mainnet-beta']
|
|
147
|
+
if (!validNetworks.includes(config.network)) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Invalid MagicBlock network '${config.network}'. ` +
|
|
150
|
+
`Valid networks: ${validNetworks.join(', ')}`
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
this.config = {
|
|
156
|
+
network: config.network ?? 'devnet',
|
|
157
|
+
region: config.region ?? 'us',
|
|
158
|
+
rpcUrl: config.rpcUrl ?? SOLANA_RPC_ENDPOINTS[config.network ?? 'devnet'],
|
|
159
|
+
debug: config.debug ?? false,
|
|
160
|
+
wallet: config.wallet,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.wallet = config.wallet
|
|
164
|
+
|
|
165
|
+
// Create standard Solana connection
|
|
166
|
+
this.connection = new Connection(this.config.rpcUrl, 'confirmed')
|
|
167
|
+
|
|
168
|
+
// Create MagicBlock router connection for TEE routing
|
|
169
|
+
const magicBlockEndpoint = MAGICBLOCK_ENDPOINTS[`${this.config.network === 'mainnet-beta' ? 'mainnet' : 'devnet'}-${this.config.region}`]
|
|
170
|
+
this.magicRouter = new ConnectionMagicRouter(magicBlockEndpoint, 'confirmed')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Set wallet keypair for signing
|
|
175
|
+
*/
|
|
176
|
+
setWallet(wallet: Keypair): void {
|
|
177
|
+
this.wallet = wallet
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if backend is available for given parameters
|
|
182
|
+
*/
|
|
183
|
+
async checkAvailability(params: BackendParams): Promise<AvailabilityResult> {
|
|
184
|
+
if (!isTransferParams(params)) {
|
|
185
|
+
// MagicBlock supports compute operations via TEE
|
|
186
|
+
return {
|
|
187
|
+
available: true,
|
|
188
|
+
estimatedTime: 1000, // ~1s for TEE execution
|
|
189
|
+
estimatedCost: 10000n, // ~0.00001 SOL
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check chain support
|
|
194
|
+
if (params.chain !== 'solana') {
|
|
195
|
+
return {
|
|
196
|
+
available: false,
|
|
197
|
+
reason: `Chain '${params.chain}' not supported. MagicBlock only works on Solana`,
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check amount validity
|
|
202
|
+
if (params.amount <= 0n) {
|
|
203
|
+
return {
|
|
204
|
+
available: false,
|
|
205
|
+
reason: 'Amount must be greater than 0',
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Check if MagicBlock endpoint is reachable
|
|
210
|
+
try {
|
|
211
|
+
await this.magicRouter.getClosestValidator()
|
|
212
|
+
} catch {
|
|
213
|
+
return {
|
|
214
|
+
available: false,
|
|
215
|
+
reason: 'MagicBlock TEE network not reachable',
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
available: true,
|
|
221
|
+
estimatedCost: this.estimateTransferCost(params),
|
|
222
|
+
estimatedTime: 2000, // ~2s for delegation + transfer + commit
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get backend capabilities
|
|
228
|
+
*/
|
|
229
|
+
getCapabilities(): BackendCapabilities {
|
|
230
|
+
return { ...MAGICBLOCK_CAPABILITIES }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Execute a privacy-preserving transfer via MagicBlock TEE
|
|
235
|
+
*
|
|
236
|
+
* Flow:
|
|
237
|
+
* 1. Delegate sender's tokens to ephemeral ATA in TEE
|
|
238
|
+
* 2. Execute private transfer inside TEE
|
|
239
|
+
* 3. Commit state back to mainnet
|
|
240
|
+
* 4. Generate SIP viewing key for compliance
|
|
241
|
+
*/
|
|
242
|
+
async execute(params: TransferParams): Promise<TransactionResult> {
|
|
243
|
+
// Validate parameters
|
|
244
|
+
const validation = await this.checkAvailability(params)
|
|
245
|
+
if (!validation.available) {
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
error: validation.reason,
|
|
249
|
+
backend: this.name,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check for native SOL (no mint) - not supported yet
|
|
254
|
+
if (!params.mint) {
|
|
255
|
+
return {
|
|
256
|
+
success: false,
|
|
257
|
+
error: 'Native SOL transfers not yet supported. Use SPL token mint.',
|
|
258
|
+
backend: this.name,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check wallet
|
|
263
|
+
const wallet = this.wallet
|
|
264
|
+
if (!wallet) {
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
error: 'Wallet keypair required for MagicBlock transfers. Set via setWallet()',
|
|
268
|
+
backend: this.name,
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const senderPubkey = new PublicKey(params.sender)
|
|
274
|
+
// Recipient pubkey stored in metadata for TEE transfer
|
|
275
|
+
const mintPubkey = new PublicKey(params.mint)
|
|
276
|
+
|
|
277
|
+
// Step 1: Delegate SPL tokens to ephemeral rollup
|
|
278
|
+
const delegateIxs = await delegateSpl(
|
|
279
|
+
senderPubkey,
|
|
280
|
+
mintPubkey,
|
|
281
|
+
params.amount,
|
|
282
|
+
{
|
|
283
|
+
payer: senderPubkey,
|
|
284
|
+
initIfMissing: true,
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
// Step 2: Create transfer instruction in TEE
|
|
289
|
+
// Note: Actual transfer happens inside TEE, this is for setup
|
|
290
|
+
const delegateTx = new Transaction().add(...delegateIxs)
|
|
291
|
+
delegateTx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash
|
|
292
|
+
delegateTx.feePayer = senderPubkey
|
|
293
|
+
|
|
294
|
+
// Sign and send via MagicBlock router
|
|
295
|
+
delegateTx.sign(wallet)
|
|
296
|
+
const delegateSig = await this.magicRouter.sendTransaction(delegateTx, [wallet])
|
|
297
|
+
|
|
298
|
+
// Log with privacy-aware logger (redacts full signature)
|
|
299
|
+
magicBlockLogger.debug('Delegation transaction sent', { signature: delegateSig })
|
|
300
|
+
|
|
301
|
+
// Step 3: Wait for delegation confirmation
|
|
302
|
+
const latestBlockhash = await this.connection.getLatestBlockhash()
|
|
303
|
+
await this.connection.confirmTransaction({
|
|
304
|
+
signature: delegateSig,
|
|
305
|
+
blockhash: latestBlockhash.blockhash,
|
|
306
|
+
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
|
307
|
+
}, 'confirmed')
|
|
308
|
+
|
|
309
|
+
// Generate SIP viewing key for compliance
|
|
310
|
+
let viewingKey: ViewingKey | undefined
|
|
311
|
+
let encryptedData: HexString | undefined
|
|
312
|
+
|
|
313
|
+
if (params.viewingKey || params.options?.generateViewingKey) {
|
|
314
|
+
viewingKey = params.viewingKey || generateViewingKey()
|
|
315
|
+
|
|
316
|
+
const txDetails = {
|
|
317
|
+
sender: params.sender,
|
|
318
|
+
recipient: params.recipient,
|
|
319
|
+
amount: params.amount.toString(),
|
|
320
|
+
token: params.mint || 'SOL',
|
|
321
|
+
timestamp: Date.now(),
|
|
322
|
+
magicblockTxId: delegateSig,
|
|
323
|
+
trustModel: 'tee' as const,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const encrypted = encryptForViewing(txDetails, viewingKey)
|
|
327
|
+
const jsonBytes = new TextEncoder().encode(JSON.stringify(encrypted))
|
|
328
|
+
encryptedData = `0x${bytesToHex(jsonBytes)}` as HexString
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
signature: delegateSig,
|
|
334
|
+
backend: this.name,
|
|
335
|
+
encryptedData,
|
|
336
|
+
metadata: {
|
|
337
|
+
delegationProgramId: DELEGATION_PROGRAM_ID.toBase58(),
|
|
338
|
+
magicProgramId: MAGIC_PROGRAM_ID.toBase58(),
|
|
339
|
+
network: this.config.network,
|
|
340
|
+
region: this.config.region,
|
|
341
|
+
viewingKeyGenerated: !!viewingKey,
|
|
342
|
+
trustModel: 'tee',
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
return {
|
|
347
|
+
success: false,
|
|
348
|
+
error: this.formatError(error),
|
|
349
|
+
backend: this.name,
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Estimate cost for a transfer
|
|
356
|
+
*/
|
|
357
|
+
async estimateCost(params: BackendParams): Promise<bigint> {
|
|
358
|
+
if (!isTransferParams(params)) {
|
|
359
|
+
return 10000n // Base cost for compute operations
|
|
360
|
+
}
|
|
361
|
+
return this.estimateTransferCost(params)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get delegation status for an account
|
|
366
|
+
*/
|
|
367
|
+
async getDelegationStatus(account: string): Promise<{ isDelegated: boolean }> {
|
|
368
|
+
return this.magicRouter.getDelegationStatus(new PublicKey(account))
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get closest TEE validator
|
|
373
|
+
*/
|
|
374
|
+
async getClosestValidator(): Promise<{ identity: string; fqdn?: string }> {
|
|
375
|
+
return this.magicRouter.getClosestValidator()
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Withdraw tokens from ephemeral rollup back to mainnet
|
|
380
|
+
*/
|
|
381
|
+
async withdraw(
|
|
382
|
+
owner: PublicKey | string,
|
|
383
|
+
mint: PublicKey | string,
|
|
384
|
+
amount: bigint
|
|
385
|
+
): Promise<TransactionInstruction> {
|
|
386
|
+
const ownerPubkey = typeof owner === 'string' ? new PublicKey(owner) : owner
|
|
387
|
+
const mintPubkey = typeof mint === 'string' ? new PublicKey(mint) : mint
|
|
388
|
+
return withdrawSplIx(ownerPubkey, mintPubkey, amount)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Derive ephemeral ATA address
|
|
393
|
+
*/
|
|
394
|
+
deriveEphemeralAta(owner: string, mint: string): [string, number] {
|
|
395
|
+
const [pda, bump] = deriveEphemeralAta(new PublicKey(owner), new PublicKey(mint))
|
|
396
|
+
return [pda.toBase58(), bump]
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Get underlying connections
|
|
401
|
+
*/
|
|
402
|
+
getConnections(): { connection: Connection; magicRouter: ConnectionMagicRouter } {
|
|
403
|
+
return {
|
|
404
|
+
connection: this.connection,
|
|
405
|
+
magicRouter: this.magicRouter,
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ─── Private Helpers ───────────────────────────────────────────────────────
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Estimate transfer cost in lamports
|
|
413
|
+
*
|
|
414
|
+
* Note: _params is reserved for dynamic cost calculation based on
|
|
415
|
+
* amount, token type, or other factors when TEE pricing becomes variable.
|
|
416
|
+
* Currently using fixed costs for simplicity.
|
|
417
|
+
*/
|
|
418
|
+
private estimateTransferCost(_params: TransferParams): bigint {
|
|
419
|
+
// Base transaction fee
|
|
420
|
+
let cost = 5000n // ~0.000005 SOL
|
|
421
|
+
|
|
422
|
+
// Delegation setup fee (first time)
|
|
423
|
+
cost += 5000n
|
|
424
|
+
|
|
425
|
+
// Account creation if needed
|
|
426
|
+
cost += 2039280n // Rent-exempt minimum for ATA
|
|
427
|
+
|
|
428
|
+
return cost
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Format error for user-friendly message
|
|
433
|
+
*/
|
|
434
|
+
private formatError(error: unknown): string {
|
|
435
|
+
if (error instanceof Error) {
|
|
436
|
+
if (error.message.includes('insufficient funds')) {
|
|
437
|
+
return 'Insufficient funds for transaction'
|
|
438
|
+
}
|
|
439
|
+
if (error.message.includes('delegation')) {
|
|
440
|
+
return 'Failed to delegate account to TEE. Check account permissions.'
|
|
441
|
+
}
|
|
442
|
+
if (error.message.includes('timeout')) {
|
|
443
|
+
return 'TEE network timeout. Try again or use a different region.'
|
|
444
|
+
}
|
|
445
|
+
return error.message
|
|
446
|
+
}
|
|
447
|
+
return 'Unknown MagicBlock error'
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Create a MagicBlock backend with default configuration
|
|
453
|
+
*/
|
|
454
|
+
export function createMagicBlockBackend(
|
|
455
|
+
config?: MagicBlockBackendConfig
|
|
456
|
+
): MagicBlockBackend {
|
|
457
|
+
return new MagicBlockBackend(config)
|
|
458
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Privacy Backend
|
|
3
|
+
*
|
|
4
|
+
* A configurable mock implementation of PrivacyBackend for testing purposes.
|
|
5
|
+
* Useful for integration tests, unit tests, and development workflows.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { MockBackend, PrivacyBackendRegistry } from '@sip-protocol/sdk'
|
|
10
|
+
*
|
|
11
|
+
* // Basic usage
|
|
12
|
+
* const mock = new MockBackend()
|
|
13
|
+
*
|
|
14
|
+
* // With custom configuration
|
|
15
|
+
* const customMock = new MockBackend({
|
|
16
|
+
* name: 'test-backend',
|
|
17
|
+
* executeResult: { success: true, signature: 'test-sig', backend: 'test-backend' },
|
|
18
|
+
* shouldFail: false,
|
|
19
|
+
* latencyMs: 100,
|
|
20
|
+
* })
|
|
21
|
+
*
|
|
22
|
+
* // Use in registry
|
|
23
|
+
* const registry = new PrivacyBackendRegistry()
|
|
24
|
+
* registry.register(mock)
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
PrivacyBackend,
|
|
30
|
+
BackendType,
|
|
31
|
+
BackendCapabilities,
|
|
32
|
+
TransferParams,
|
|
33
|
+
TransactionResult,
|
|
34
|
+
AvailabilityResult,
|
|
35
|
+
BackendParams,
|
|
36
|
+
PrivacyBackendVersion,
|
|
37
|
+
} from './interface'
|
|
38
|
+
import { CURRENT_BACKEND_VERSION } from './interface'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Configuration options for MockBackend
|
|
42
|
+
*/
|
|
43
|
+
export interface MockBackendConfig {
|
|
44
|
+
/** Backend name (default: 'mock') */
|
|
45
|
+
name?: string
|
|
46
|
+
/** Backend type (default: 'transaction') */
|
|
47
|
+
type?: BackendType
|
|
48
|
+
/** Supported chains (default: ['solana', 'ethereum']) */
|
|
49
|
+
chains?: string[]
|
|
50
|
+
/** Custom capabilities to return */
|
|
51
|
+
capabilities?: Partial<BackendCapabilities>
|
|
52
|
+
/** Custom availability result */
|
|
53
|
+
availabilityResult?: Partial<AvailabilityResult>
|
|
54
|
+
/** Custom execute result */
|
|
55
|
+
executeResult?: Partial<TransactionResult>
|
|
56
|
+
/** Should execute fail with an error */
|
|
57
|
+
shouldFail?: boolean
|
|
58
|
+
/** Error message when shouldFail is true */
|
|
59
|
+
failureMessage?: string
|
|
60
|
+
/** Simulated latency in milliseconds (default: 0) */
|
|
61
|
+
latencyMs?: number
|
|
62
|
+
/** Estimated cost in lamports (default: 5000n) */
|
|
63
|
+
estimatedCost?: bigint
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Default mock capabilities
|
|
68
|
+
*/
|
|
69
|
+
const DEFAULT_CAPABILITIES: BackendCapabilities = {
|
|
70
|
+
hiddenAmount: true,
|
|
71
|
+
hiddenSender: true,
|
|
72
|
+
hiddenRecipient: true,
|
|
73
|
+
hiddenCompute: false,
|
|
74
|
+
complianceSupport: true,
|
|
75
|
+
setupRequired: false,
|
|
76
|
+
latencyEstimate: 'fast',
|
|
77
|
+
supportedTokens: 'all',
|
|
78
|
+
minAmount: undefined,
|
|
79
|
+
maxAmount: undefined,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Mock Privacy Backend for testing
|
|
84
|
+
*
|
|
85
|
+
* Provides a configurable mock implementation that can simulate
|
|
86
|
+
* various backend behaviors for testing purposes.
|
|
87
|
+
*/
|
|
88
|
+
export class MockBackend implements PrivacyBackend {
|
|
89
|
+
readonly version: PrivacyBackendVersion = CURRENT_BACKEND_VERSION
|
|
90
|
+
readonly name: string
|
|
91
|
+
readonly type: BackendType
|
|
92
|
+
readonly chains: string[]
|
|
93
|
+
|
|
94
|
+
private config: Required<Omit<MockBackendConfig, 'name' | 'type' | 'chains' | 'capabilities' | 'availabilityResult' | 'executeResult'>>
|
|
95
|
+
private capabilities: BackendCapabilities
|
|
96
|
+
private availabilityResult: AvailabilityResult
|
|
97
|
+
private executeResult: TransactionResult
|
|
98
|
+
|
|
99
|
+
/** Track number of execute calls */
|
|
100
|
+
public executeCalls: TransferParams[] = []
|
|
101
|
+
|
|
102
|
+
/** Track number of checkAvailability calls */
|
|
103
|
+
public availabilityCalls: BackendParams[] = []
|
|
104
|
+
|
|
105
|
+
constructor(config: MockBackendConfig = {}) {
|
|
106
|
+
this.name = config.name ?? 'mock'
|
|
107
|
+
this.type = config.type ?? 'transaction'
|
|
108
|
+
this.chains = config.chains ?? ['solana', 'ethereum']
|
|
109
|
+
|
|
110
|
+
this.config = {
|
|
111
|
+
shouldFail: config.shouldFail ?? false,
|
|
112
|
+
failureMessage: config.failureMessage ?? 'Mock backend failure',
|
|
113
|
+
latencyMs: config.latencyMs ?? 0,
|
|
114
|
+
estimatedCost: config.estimatedCost ?? 5000n,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.capabilities = {
|
|
118
|
+
...DEFAULT_CAPABILITIES,
|
|
119
|
+
...config.capabilities,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.availabilityResult = {
|
|
123
|
+
available: true,
|
|
124
|
+
estimatedCost: this.config.estimatedCost,
|
|
125
|
+
estimatedTime: this.config.latencyMs || 1000,
|
|
126
|
+
...config.availabilityResult,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.executeResult = {
|
|
130
|
+
success: true,
|
|
131
|
+
signature: `mock-sig-${Date.now()}`,
|
|
132
|
+
backend: this.name,
|
|
133
|
+
...config.executeResult,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check if backend is available for given parameters
|
|
139
|
+
*/
|
|
140
|
+
async checkAvailability(params: BackendParams): Promise<AvailabilityResult> {
|
|
141
|
+
this.availabilityCalls.push(params)
|
|
142
|
+
|
|
143
|
+
if (this.config.latencyMs > 0) {
|
|
144
|
+
await this.delay(this.config.latencyMs)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check if chain is supported
|
|
148
|
+
if ('chain' in params && !this.chains.includes(params.chain)) {
|
|
149
|
+
return {
|
|
150
|
+
available: false,
|
|
151
|
+
reason: `Chain ${params.chain} not supported by ${this.name}`,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { ...this.availabilityResult }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get backend capabilities
|
|
160
|
+
*/
|
|
161
|
+
getCapabilities(): BackendCapabilities {
|
|
162
|
+
return { ...this.capabilities }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Execute a privacy-preserving transfer
|
|
167
|
+
*/
|
|
168
|
+
async execute(params: TransferParams): Promise<TransactionResult> {
|
|
169
|
+
this.executeCalls.push(params)
|
|
170
|
+
|
|
171
|
+
if (this.config.latencyMs > 0) {
|
|
172
|
+
await this.delay(this.config.latencyMs)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (this.config.shouldFail) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: this.config.failureMessage,
|
|
179
|
+
backend: this.name,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
...this.executeResult,
|
|
185
|
+
signature: `mock-sig-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Estimate cost for an operation
|
|
191
|
+
*/
|
|
192
|
+
async estimateCost(_params: BackendParams): Promise<bigint> {
|
|
193
|
+
if (this.config.latencyMs > 0) {
|
|
194
|
+
await this.delay(this.config.latencyMs)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return this.config.estimatedCost
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Reset mock state (call counts, etc.)
|
|
202
|
+
*/
|
|
203
|
+
reset(): void {
|
|
204
|
+
this.executeCalls = []
|
|
205
|
+
this.availabilityCalls = []
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Configure mock to fail on next execute
|
|
210
|
+
*/
|
|
211
|
+
setFailure(shouldFail: boolean, message?: string): void {
|
|
212
|
+
this.config.shouldFail = shouldFail
|
|
213
|
+
if (message) {
|
|
214
|
+
this.config.failureMessage = message
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Update availability result
|
|
220
|
+
*/
|
|
221
|
+
setAvailability(available: boolean, reason?: string): void {
|
|
222
|
+
this.availabilityResult = {
|
|
223
|
+
...this.availabilityResult,
|
|
224
|
+
available,
|
|
225
|
+
reason,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Helper to simulate latency
|
|
231
|
+
*/
|
|
232
|
+
private delay(ms: number): Promise<void> {
|
|
233
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Create a mock backend factory for parameterized tests
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* const createMock = createMockFactory({ latencyMs: 50 })
|
|
243
|
+
*
|
|
244
|
+
* const fast = createMock('fast-backend')
|
|
245
|
+
* const slow = createMock('slow-backend', { latencyMs: 500 })
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export function createMockFactory(
|
|
249
|
+
defaultConfig: MockBackendConfig = {}
|
|
250
|
+
): (name: string, overrides?: MockBackendConfig) => MockBackend {
|
|
251
|
+
return (name: string, overrides: MockBackendConfig = {}) => {
|
|
252
|
+
return new MockBackend({
|
|
253
|
+
...defaultConfig,
|
|
254
|
+
...overrides,
|
|
255
|
+
name,
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
}
|