@sip-protocol/sdk 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-Q4NAVBTS.mjs +0 -46
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartRouter - Privacy Backend Selection
|
|
3
|
+
*
|
|
4
|
+
* Automatically selects the optimal privacy backend based on:
|
|
5
|
+
* - User preferences (privacy, speed, cost, compliance)
|
|
6
|
+
* - Backend capabilities and availability
|
|
7
|
+
* - Transfer parameters
|
|
8
|
+
* - Backend health status (circuit breaker)
|
|
9
|
+
*
|
|
10
|
+
* ## Features
|
|
11
|
+
*
|
|
12
|
+
* - **Health-aware selection**: Skips backends with open circuit breakers
|
|
13
|
+
* - **Automatic fallback**: Tries alternatives when primary backend fails
|
|
14
|
+
* - **Metrics tracking**: Records success/failure for health monitoring
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { SmartRouter, PrivacyBackendRegistry, SIPNativeBackend } from '@sip-protocol/sdk'
|
|
19
|
+
*
|
|
20
|
+
* const registry = new PrivacyBackendRegistry()
|
|
21
|
+
* registry.register(new SIPNativeBackend())
|
|
22
|
+
*
|
|
23
|
+
* const router = new SmartRouter(registry)
|
|
24
|
+
*
|
|
25
|
+
* // Auto-select best backend with fallback
|
|
26
|
+
* const result = await router.execute(params, {
|
|
27
|
+
* prioritize: 'compliance',
|
|
28
|
+
* requireViewingKeys: true,
|
|
29
|
+
* enableFallback: true,
|
|
30
|
+
* })
|
|
31
|
+
*
|
|
32
|
+
* // Or just select without executing
|
|
33
|
+
* const selection = await router.selectBackend(params, config)
|
|
34
|
+
* console.log(`Selected: ${selection.backend.name}`)
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import type {
|
|
39
|
+
PrivacyBackend,
|
|
40
|
+
TransferParams,
|
|
41
|
+
TransactionResult,
|
|
42
|
+
ComputationParams,
|
|
43
|
+
ComputationResult,
|
|
44
|
+
SmartRouterConfig,
|
|
45
|
+
BackendSelectionResult,
|
|
46
|
+
AvailabilityResult,
|
|
47
|
+
} from './interface'
|
|
48
|
+
import { AllBackendsFailedError } from './interface'
|
|
49
|
+
import { PrivacyBackendRegistry } from './registry'
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Default router configuration
|
|
53
|
+
*/
|
|
54
|
+
const DEFAULT_CONFIG: SmartRouterConfig = {
|
|
55
|
+
prioritize: 'privacy',
|
|
56
|
+
requireViewingKeys: false,
|
|
57
|
+
allowComputePrivacy: true,
|
|
58
|
+
enableFallback: true,
|
|
59
|
+
includeUnhealthy: false,
|
|
60
|
+
maxFallbackAttempts: 3,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Scoring weights for different priorities
|
|
65
|
+
*/
|
|
66
|
+
const PRIORITY_WEIGHTS = {
|
|
67
|
+
privacy: {
|
|
68
|
+
hiddenAmount: 25,
|
|
69
|
+
hiddenSender: 25,
|
|
70
|
+
hiddenRecipient: 25,
|
|
71
|
+
hiddenCompute: 10,
|
|
72
|
+
anonymitySet: 15,
|
|
73
|
+
},
|
|
74
|
+
speed: {
|
|
75
|
+
fast: 40,
|
|
76
|
+
medium: 25,
|
|
77
|
+
slow: 10,
|
|
78
|
+
setupRequired: -20,
|
|
79
|
+
},
|
|
80
|
+
cost: {
|
|
81
|
+
baseCost: 50,
|
|
82
|
+
estimatedCost: 50,
|
|
83
|
+
},
|
|
84
|
+
compliance: {
|
|
85
|
+
complianceSupport: 60,
|
|
86
|
+
hiddenAmount: 15,
|
|
87
|
+
hiddenSender: 15,
|
|
88
|
+
hiddenRecipient: 10,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* SmartRouter for automatic backend selection
|
|
94
|
+
*
|
|
95
|
+
* Analyzes available backends and selects the optimal one
|
|
96
|
+
* based on user preferences and transfer requirements.
|
|
97
|
+
*/
|
|
98
|
+
export class SmartRouter {
|
|
99
|
+
private registry: PrivacyBackendRegistry
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a new SmartRouter
|
|
103
|
+
*
|
|
104
|
+
* @param registry - Backend registry to use for selection
|
|
105
|
+
*/
|
|
106
|
+
constructor(registry: PrivacyBackendRegistry) {
|
|
107
|
+
this.registry = registry
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Select the best backend for a transfer
|
|
112
|
+
*
|
|
113
|
+
* @param params - Transfer parameters
|
|
114
|
+
* @param config - Router configuration
|
|
115
|
+
* @returns Selection result with backend and reasoning
|
|
116
|
+
* @throws Error if no suitable backend is found
|
|
117
|
+
*/
|
|
118
|
+
async selectBackend(
|
|
119
|
+
params: TransferParams,
|
|
120
|
+
config: Partial<SmartRouterConfig> = {}
|
|
121
|
+
): Promise<BackendSelectionResult> {
|
|
122
|
+
const fullConfig = { ...DEFAULT_CONFIG, ...config }
|
|
123
|
+
|
|
124
|
+
// Get backends for the chain
|
|
125
|
+
const chainBackends = this.registry.getByChain(params.chain)
|
|
126
|
+
|
|
127
|
+
if (chainBackends.length === 0) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`No backends available for chain '${params.chain}'. ` +
|
|
130
|
+
`Register a backend that supports this chain.`
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Filter and score backends
|
|
135
|
+
const scoredBackends: Array<{
|
|
136
|
+
backend: PrivacyBackend
|
|
137
|
+
availability: AvailabilityResult
|
|
138
|
+
score: number
|
|
139
|
+
reason: string
|
|
140
|
+
}> = []
|
|
141
|
+
|
|
142
|
+
for (const backend of chainBackends) {
|
|
143
|
+
// Check exclusions
|
|
144
|
+
if (fullConfig.excludeBackends?.includes(backend.name)) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check health status (circuit breaker)
|
|
149
|
+
if (!fullConfig.includeUnhealthy && !this.registry.isHealthy(backend.name)) {
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check availability
|
|
154
|
+
const availability = await backend.checkAvailability(params)
|
|
155
|
+
if (!availability.available) {
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check hard requirements
|
|
160
|
+
const capabilities = backend.getCapabilities()
|
|
161
|
+
|
|
162
|
+
// Viewing key requirement
|
|
163
|
+
if (fullConfig.requireViewingKeys && !capabilities.complianceSupport) {
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Anonymity set requirement
|
|
168
|
+
if (
|
|
169
|
+
fullConfig.minAnonymitySet &&
|
|
170
|
+
capabilities.anonymitySet !== undefined &&
|
|
171
|
+
capabilities.anonymitySet < fullConfig.minAnonymitySet
|
|
172
|
+
) {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Compute privacy filter
|
|
177
|
+
if (!fullConfig.allowComputePrivacy && backend.type === 'compute') {
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Cost limit
|
|
182
|
+
if (fullConfig.maxCost && availability.estimatedCost) {
|
|
183
|
+
if (availability.estimatedCost > fullConfig.maxCost) {
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Latency limit
|
|
189
|
+
if (fullConfig.maxLatency && availability.estimatedTime) {
|
|
190
|
+
if (availability.estimatedTime > fullConfig.maxLatency) {
|
|
191
|
+
continue
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Score this backend
|
|
196
|
+
const { score, reason } = this.scoreBackend(
|
|
197
|
+
backend,
|
|
198
|
+
availability,
|
|
199
|
+
fullConfig
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
scoredBackends.push({
|
|
203
|
+
backend,
|
|
204
|
+
availability,
|
|
205
|
+
score,
|
|
206
|
+
reason,
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (scoredBackends.length === 0) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`No backends meet the requirements for this transfer. ` +
|
|
213
|
+
`Check your router configuration and registered backends.`
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Sort by score (descending)
|
|
218
|
+
scoredBackends.sort((a, b) => b.score - a.score)
|
|
219
|
+
|
|
220
|
+
// Preferred backend bonus
|
|
221
|
+
if (fullConfig.preferredBackend) {
|
|
222
|
+
const preferredIndex = scoredBackends.findIndex(
|
|
223
|
+
s => s.backend.name === fullConfig.preferredBackend
|
|
224
|
+
)
|
|
225
|
+
if (preferredIndex > 0) {
|
|
226
|
+
// Move preferred to top if within 10 points of leader
|
|
227
|
+
const preferred = scoredBackends[preferredIndex]
|
|
228
|
+
const leader = scoredBackends[0]
|
|
229
|
+
if (leader.score - preferred.score <= 10) {
|
|
230
|
+
scoredBackends.splice(preferredIndex, 1)
|
|
231
|
+
scoredBackends.unshift(preferred)
|
|
232
|
+
preferred.reason = `Preferred backend (within 10pts of optimal)`
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const selected = scoredBackends[0]
|
|
238
|
+
const alternatives = scoredBackends.slice(1).map(s => ({
|
|
239
|
+
backend: s.backend,
|
|
240
|
+
score: s.score,
|
|
241
|
+
reason: s.reason,
|
|
242
|
+
}))
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
backend: selected.backend,
|
|
246
|
+
reason: selected.reason,
|
|
247
|
+
alternatives,
|
|
248
|
+
score: selected.score,
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Execute a transfer using the best available backend
|
|
254
|
+
*
|
|
255
|
+
* Includes automatic fallback to alternatives if the primary backend fails
|
|
256
|
+
* (when enableFallback is true). Records success/failure for health tracking.
|
|
257
|
+
*
|
|
258
|
+
* @param params - Transfer parameters
|
|
259
|
+
* @param config - Router configuration
|
|
260
|
+
* @returns Transaction result
|
|
261
|
+
* @throws AllBackendsFailedError if all backends fail
|
|
262
|
+
*/
|
|
263
|
+
async execute(
|
|
264
|
+
params: TransferParams,
|
|
265
|
+
config: Partial<SmartRouterConfig> = {}
|
|
266
|
+
): Promise<TransactionResult> {
|
|
267
|
+
const fullConfig = {
|
|
268
|
+
...DEFAULT_CONFIG,
|
|
269
|
+
...config,
|
|
270
|
+
allowComputePrivacy: false, // Only transaction backends for execute()
|
|
271
|
+
}
|
|
272
|
+
const selection = await this.selectBackend(params, fullConfig)
|
|
273
|
+
|
|
274
|
+
// Try primary backend
|
|
275
|
+
const result = await this.executeOnBackend(selection.backend, params)
|
|
276
|
+
|
|
277
|
+
if (result.success) {
|
|
278
|
+
return result
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Primary failed, try fallback if enabled
|
|
282
|
+
if (!fullConfig.enableFallback || selection.alternatives.length === 0) {
|
|
283
|
+
return result
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Try alternatives
|
|
287
|
+
const attemptedBackends = [selection.backend.name]
|
|
288
|
+
const errors = new Map<string, string>()
|
|
289
|
+
errors.set(selection.backend.name, result.error || 'Unknown error')
|
|
290
|
+
|
|
291
|
+
const maxFallbackAttempts = fullConfig.maxFallbackAttempts ?? 3
|
|
292
|
+
|
|
293
|
+
// Iterate over all alternatives, but limit actual attempts
|
|
294
|
+
// Skipped backends (unhealthy/already attempted) don't count against the limit
|
|
295
|
+
let actualAttempts = 0
|
|
296
|
+
for (
|
|
297
|
+
let i = 0;
|
|
298
|
+
i < selection.alternatives.length && actualAttempts < maxFallbackAttempts;
|
|
299
|
+
i++
|
|
300
|
+
) {
|
|
301
|
+
const alternative = selection.alternatives[i]
|
|
302
|
+
|
|
303
|
+
// Skip if already attempted (defensive - shouldn't happen with unique names)
|
|
304
|
+
if (attemptedBackends.includes(alternative.backend.name)) {
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Skip unhealthy backends unless explicitly included
|
|
309
|
+
if (!fullConfig.includeUnhealthy && !this.registry.isHealthy(alternative.backend.name)) {
|
|
310
|
+
continue
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// This counts as an actual attempt
|
|
314
|
+
actualAttempts++
|
|
315
|
+
attemptedBackends.push(alternative.backend.name)
|
|
316
|
+
const fallbackResult = await this.executeOnBackend(alternative.backend, params)
|
|
317
|
+
|
|
318
|
+
if (fallbackResult.success) {
|
|
319
|
+
// Add metadata about fallback
|
|
320
|
+
return {
|
|
321
|
+
...fallbackResult,
|
|
322
|
+
metadata: {
|
|
323
|
+
...fallbackResult.metadata,
|
|
324
|
+
fallbackFrom: selection.backend.name,
|
|
325
|
+
attemptedBackends,
|
|
326
|
+
},
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
errors.set(alternative.backend.name, fallbackResult.error || 'Unknown error')
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// All attempts failed
|
|
334
|
+
throw new AllBackendsFailedError(attemptedBackends, errors, params)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Execute on a specific backend with health tracking
|
|
339
|
+
*
|
|
340
|
+
* @param backend - Backend to execute on
|
|
341
|
+
* @param params - Transfer parameters
|
|
342
|
+
* @returns Transaction result (never throws, returns error in result)
|
|
343
|
+
*/
|
|
344
|
+
private async executeOnBackend(
|
|
345
|
+
backend: PrivacyBackend,
|
|
346
|
+
params: TransferParams
|
|
347
|
+
): Promise<TransactionResult> {
|
|
348
|
+
const startTime = Date.now()
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const result = await backend.execute(params)
|
|
352
|
+
const latency = Date.now() - startTime
|
|
353
|
+
|
|
354
|
+
if (result.success) {
|
|
355
|
+
this.registry.recordSuccess(backend.name, latency)
|
|
356
|
+
} else {
|
|
357
|
+
this.registry.recordFailure(backend.name, result.error || 'Execution returned failure')
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return result
|
|
361
|
+
} catch (error) {
|
|
362
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
363
|
+
this.registry.recordFailure(backend.name, errorMessage)
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
success: false,
|
|
367
|
+
error: errorMessage,
|
|
368
|
+
backend: backend.name,
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Execute a computation using the best available compute backend
|
|
375
|
+
*
|
|
376
|
+
* Selects from compute-type backends (Arcium, Inco) and executes
|
|
377
|
+
* the computation via MPC/FHE.
|
|
378
|
+
*
|
|
379
|
+
* @param params - Computation parameters
|
|
380
|
+
* @param config - Router configuration
|
|
381
|
+
* @returns Computation result
|
|
382
|
+
* @throws Error if no compute backend is available or supports the computation
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* const result = await router.executeComputation({
|
|
387
|
+
* chain: 'solana',
|
|
388
|
+
* circuitId: 'private-swap',
|
|
389
|
+
* encryptedInputs: [encryptedAmount, encryptedPrice],
|
|
390
|
+
* })
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
async executeComputation(
|
|
394
|
+
params: ComputationParams,
|
|
395
|
+
config: Partial<SmartRouterConfig> = {}
|
|
396
|
+
): Promise<ComputationResult> {
|
|
397
|
+
const selection = await this.selectComputeBackend(params, config)
|
|
398
|
+
|
|
399
|
+
if (!selection.backend.executeComputation) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`Backend '${selection.backend.name}' does not support compute operations. ` +
|
|
402
|
+
`This should not happen - please report this bug.`
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return selection.backend.executeComputation(params)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Select the best compute backend for a computation
|
|
411
|
+
*
|
|
412
|
+
* @param params - Computation parameters
|
|
413
|
+
* @param config - Router configuration
|
|
414
|
+
* @returns Selection result with backend and reasoning
|
|
415
|
+
* @throws Error if no suitable compute backend is found
|
|
416
|
+
*/
|
|
417
|
+
async selectComputeBackend(
|
|
418
|
+
params: ComputationParams,
|
|
419
|
+
config: Partial<SmartRouterConfig> = {}
|
|
420
|
+
): Promise<BackendSelectionResult> {
|
|
421
|
+
const fullConfig = { ...DEFAULT_CONFIG, ...config }
|
|
422
|
+
|
|
423
|
+
// Get all backends for the chain
|
|
424
|
+
const chainBackends = this.registry.getByChain(params.chain)
|
|
425
|
+
|
|
426
|
+
// Filter to compute backends only
|
|
427
|
+
const computeBackends = chainBackends.filter(
|
|
428
|
+
b => b.type === 'compute' || b.type === 'both'
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
if (computeBackends.length === 0) {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`No compute backends available for chain '${params.chain}'. ` +
|
|
434
|
+
`Register a compute backend (e.g., ArciumBackend) that supports this chain.`
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Filter and score backends
|
|
439
|
+
const scoredBackends: Array<{
|
|
440
|
+
backend: PrivacyBackend
|
|
441
|
+
availability: AvailabilityResult
|
|
442
|
+
score: number
|
|
443
|
+
reason: string
|
|
444
|
+
}> = []
|
|
445
|
+
|
|
446
|
+
for (const backend of computeBackends) {
|
|
447
|
+
// Check exclusions
|
|
448
|
+
if (fullConfig.excludeBackends?.includes(backend.name)) {
|
|
449
|
+
continue
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Check availability
|
|
453
|
+
const availability = await backend.checkAvailability(params)
|
|
454
|
+
if (!availability.available) {
|
|
455
|
+
continue
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Check cost limit
|
|
459
|
+
if (fullConfig.maxCost && availability.estimatedCost) {
|
|
460
|
+
if (availability.estimatedCost > fullConfig.maxCost) {
|
|
461
|
+
continue
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Check latency limit
|
|
466
|
+
if (fullConfig.maxLatency && availability.estimatedTime) {
|
|
467
|
+
if (availability.estimatedTime > fullConfig.maxLatency) {
|
|
468
|
+
continue
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Score this backend
|
|
473
|
+
const { score, reason } = this.scoreBackend(
|
|
474
|
+
backend,
|
|
475
|
+
availability,
|
|
476
|
+
fullConfig
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
scoredBackends.push({
|
|
480
|
+
backend,
|
|
481
|
+
availability,
|
|
482
|
+
score,
|
|
483
|
+
reason,
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (scoredBackends.length === 0) {
|
|
488
|
+
throw new Error(
|
|
489
|
+
`No compute backends meet the requirements for this computation. ` +
|
|
490
|
+
`Check that the circuit exists and the cluster is available.`
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Sort by score (descending)
|
|
495
|
+
scoredBackends.sort((a, b) => b.score - a.score)
|
|
496
|
+
|
|
497
|
+
// Preferred backend bonus
|
|
498
|
+
if (fullConfig.preferredBackend) {
|
|
499
|
+
const preferredIndex = scoredBackends.findIndex(
|
|
500
|
+
s => s.backend.name === fullConfig.preferredBackend
|
|
501
|
+
)
|
|
502
|
+
if (preferredIndex > 0) {
|
|
503
|
+
const preferred = scoredBackends[preferredIndex]
|
|
504
|
+
const leader = scoredBackends[0]
|
|
505
|
+
if (leader.score - preferred.score <= 10) {
|
|
506
|
+
scoredBackends.splice(preferredIndex, 1)
|
|
507
|
+
scoredBackends.unshift(preferred)
|
|
508
|
+
preferred.reason = `Preferred backend (within 10pts of optimal)`
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const selected = scoredBackends[0]
|
|
514
|
+
const alternatives = scoredBackends.slice(1).map(s => ({
|
|
515
|
+
backend: s.backend,
|
|
516
|
+
score: s.score,
|
|
517
|
+
reason: s.reason,
|
|
518
|
+
}))
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
backend: selected.backend,
|
|
522
|
+
reason: selected.reason,
|
|
523
|
+
alternatives,
|
|
524
|
+
score: selected.score,
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Get available backends for a transfer (without selecting)
|
|
530
|
+
*
|
|
531
|
+
* @param params - Transfer parameters
|
|
532
|
+
* @returns Array of available backends with scores
|
|
533
|
+
*/
|
|
534
|
+
async getAvailableBackends(
|
|
535
|
+
params: TransferParams
|
|
536
|
+
): Promise<Array<{ backend: PrivacyBackend; availability: AvailabilityResult }>> {
|
|
537
|
+
return this.registry.findAvailable(params)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Score a backend based on configuration priority
|
|
542
|
+
*/
|
|
543
|
+
private scoreBackend(
|
|
544
|
+
backend: PrivacyBackend,
|
|
545
|
+
availability: AvailabilityResult,
|
|
546
|
+
config: SmartRouterConfig
|
|
547
|
+
): { score: number; reason: string } {
|
|
548
|
+
const capabilities = backend.getCapabilities()
|
|
549
|
+
let score = 0
|
|
550
|
+
const reasons: string[] = []
|
|
551
|
+
|
|
552
|
+
switch (config.prioritize) {
|
|
553
|
+
case 'privacy':
|
|
554
|
+
if (capabilities.hiddenAmount) {
|
|
555
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenAmount
|
|
556
|
+
reasons.push('hidden amounts')
|
|
557
|
+
}
|
|
558
|
+
if (capabilities.hiddenSender) {
|
|
559
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenSender
|
|
560
|
+
reasons.push('hidden sender')
|
|
561
|
+
}
|
|
562
|
+
if (capabilities.hiddenRecipient) {
|
|
563
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenRecipient
|
|
564
|
+
reasons.push('hidden recipient')
|
|
565
|
+
}
|
|
566
|
+
if (capabilities.hiddenCompute) {
|
|
567
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenCompute
|
|
568
|
+
reasons.push('private compute')
|
|
569
|
+
}
|
|
570
|
+
if (capabilities.anonymitySet && capabilities.anonymitySet >= 100) {
|
|
571
|
+
score += PRIORITY_WEIGHTS.privacy.anonymitySet
|
|
572
|
+
reasons.push(`anonymity set: ${capabilities.anonymitySet}`)
|
|
573
|
+
}
|
|
574
|
+
break
|
|
575
|
+
|
|
576
|
+
case 'speed':
|
|
577
|
+
score += PRIORITY_WEIGHTS.speed[capabilities.latencyEstimate]
|
|
578
|
+
reasons.push(`${capabilities.latencyEstimate} latency`)
|
|
579
|
+
if (capabilities.setupRequired) {
|
|
580
|
+
score += PRIORITY_WEIGHTS.speed.setupRequired
|
|
581
|
+
reasons.push('setup required')
|
|
582
|
+
}
|
|
583
|
+
break
|
|
584
|
+
|
|
585
|
+
case 'cost':
|
|
586
|
+
// Lower cost = higher score (invert with log scale)
|
|
587
|
+
if (availability.estimatedCost !== undefined) {
|
|
588
|
+
// Use log scale to handle wide range of costs
|
|
589
|
+
// log10(100) ≈ 2, log10(100000) ≈ 5, log10(1000000) ≈ 6
|
|
590
|
+
const logCost = availability.estimatedCost > BigInt(0)
|
|
591
|
+
? Math.log10(Number(availability.estimatedCost))
|
|
592
|
+
: 0
|
|
593
|
+
// Max cost assumed around 10^14 (log = 14), gives score 0
|
|
594
|
+
// Min cost around 10^2 (log = 2), gives score ~60
|
|
595
|
+
const costScore = Math.max(0, 70 - logCost * 5)
|
|
596
|
+
score += costScore
|
|
597
|
+
reasons.push(`low cost`)
|
|
598
|
+
}
|
|
599
|
+
break
|
|
600
|
+
|
|
601
|
+
case 'compliance':
|
|
602
|
+
if (capabilities.complianceSupport) {
|
|
603
|
+
score += PRIORITY_WEIGHTS.compliance.complianceSupport
|
|
604
|
+
reasons.push('viewing key support')
|
|
605
|
+
}
|
|
606
|
+
if (capabilities.hiddenAmount) {
|
|
607
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenAmount
|
|
608
|
+
}
|
|
609
|
+
if (capabilities.hiddenSender) {
|
|
610
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenSender
|
|
611
|
+
}
|
|
612
|
+
if (capabilities.hiddenRecipient) {
|
|
613
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenRecipient
|
|
614
|
+
}
|
|
615
|
+
break
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Normalize score to 0-100
|
|
619
|
+
score = Math.min(100, Math.max(0, score))
|
|
620
|
+
|
|
621
|
+
return {
|
|
622
|
+
score,
|
|
623
|
+
reason: reasons.length > 0 ? reasons.join(', ') : 'default selection',
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|