@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,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helius RPC Provider
|
|
3
|
+
*
|
|
4
|
+
* Leverages Helius DAS (Digital Asset Standard) API for efficient
|
|
5
|
+
* token balance queries and asset metadata.
|
|
6
|
+
*
|
|
7
|
+
* @see https://docs.helius.dev/solana-apis/digital-asset-standard-das-api
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { HeliusProvider } from '@sip-protocol/sdk'
|
|
12
|
+
*
|
|
13
|
+
* const helius = new HeliusProvider({
|
|
14
|
+
* apiKey: process.env.HELIUS_API_KEY!,
|
|
15
|
+
* cluster: 'devnet'
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* const assets = await helius.getAssetsByOwner('7xK9...')
|
|
19
|
+
* console.log(assets) // [{ mint: '...', amount: 1000000n, decimals: 6, symbol: 'USDC' }]
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { SolanaRPCProvider, TokenAsset, ProviderConfig } from './interface'
|
|
24
|
+
import { ValidationError, NetworkError } from '../../../errors'
|
|
25
|
+
import {
|
|
26
|
+
SOLANA_ADDRESS_MIN_LENGTH,
|
|
27
|
+
SOLANA_ADDRESS_MAX_LENGTH,
|
|
28
|
+
HELIUS_API_KEY_MIN_LENGTH,
|
|
29
|
+
HELIUS_DAS_PAGE_LIMIT,
|
|
30
|
+
HELIUS_MAX_PAGES,
|
|
31
|
+
sanitizeUrl,
|
|
32
|
+
} from '../constants'
|
|
33
|
+
|
|
34
|
+
/** Default fetch timeout in milliseconds */
|
|
35
|
+
const DEFAULT_FETCH_TIMEOUT_MS = 30000
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mask API key for safe logging/error messages
|
|
39
|
+
*
|
|
40
|
+
* Shows only first 4 and last 4 characters to prevent key exposure.
|
|
41
|
+
*
|
|
42
|
+
* @param apiKey - Helius API key to mask
|
|
43
|
+
* @returns Masked key (e.g., 'abcd...wxyz') or '***' if too short
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
function maskApiKey(apiKey: string): string {
|
|
47
|
+
if (apiKey.length <= HELIUS_API_KEY_MIN_LENGTH) return '***'
|
|
48
|
+
return `${apiKey.slice(0, 4)}...${apiKey.slice(-4)}`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Fetch with configurable timeout using AbortController
|
|
53
|
+
*
|
|
54
|
+
* Wraps fetch with a timeout to prevent hanging requests.
|
|
55
|
+
*
|
|
56
|
+
* @param url - URL to fetch
|
|
57
|
+
* @param options - Fetch options (method, headers, body, etc.)
|
|
58
|
+
* @param timeoutMs - Timeout in milliseconds (default: 30000)
|
|
59
|
+
* @returns Fetch response
|
|
60
|
+
* @throws NetworkError if request times out
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
async function fetchWithTimeout(
|
|
64
|
+
url: string,
|
|
65
|
+
options: RequestInit,
|
|
66
|
+
timeoutMs: number = DEFAULT_FETCH_TIMEOUT_MS
|
|
67
|
+
): Promise<Response> {
|
|
68
|
+
const controller = new AbortController()
|
|
69
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(url, {
|
|
73
|
+
...options,
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
})
|
|
76
|
+
return response
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
79
|
+
// H-2 FIX: Sanitize URL to prevent credential exposure
|
|
80
|
+
throw new NetworkError(
|
|
81
|
+
`Request timeout after ${timeoutMs}ms`,
|
|
82
|
+
undefined,
|
|
83
|
+
{ endpoint: sanitizeUrl(url) }
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
throw error
|
|
87
|
+
} finally {
|
|
88
|
+
clearTimeout(timeoutId)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Helius API response types
|
|
94
|
+
*/
|
|
95
|
+
interface HeliusDASAsset {
|
|
96
|
+
id: string
|
|
97
|
+
interface: string
|
|
98
|
+
content?: {
|
|
99
|
+
metadata?: {
|
|
100
|
+
name?: string
|
|
101
|
+
symbol?: string
|
|
102
|
+
}
|
|
103
|
+
links?: {
|
|
104
|
+
image?: string
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
token_info?: {
|
|
108
|
+
/** Balance as string to preserve precision for large values */
|
|
109
|
+
balance?: string | number
|
|
110
|
+
decimals?: number
|
|
111
|
+
symbol?: string
|
|
112
|
+
token_program?: string
|
|
113
|
+
mint_authority?: string
|
|
114
|
+
freeze_authority?: string
|
|
115
|
+
}
|
|
116
|
+
ownership?: {
|
|
117
|
+
owner: string
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface HeliusDASResponse {
|
|
122
|
+
jsonrpc: string
|
|
123
|
+
result?: {
|
|
124
|
+
items: HeliusDASAsset[]
|
|
125
|
+
total: number
|
|
126
|
+
limit: number
|
|
127
|
+
page: number
|
|
128
|
+
cursor?: string
|
|
129
|
+
}
|
|
130
|
+
error?: {
|
|
131
|
+
code: number
|
|
132
|
+
message: string
|
|
133
|
+
}
|
|
134
|
+
id: string
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface HeliusBalancesResponse {
|
|
138
|
+
tokens: Array<{
|
|
139
|
+
mint: string
|
|
140
|
+
amount: number
|
|
141
|
+
decimals: number
|
|
142
|
+
tokenAccount: string
|
|
143
|
+
}>
|
|
144
|
+
nativeBalance: number
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Helius provider configuration
|
|
149
|
+
*
|
|
150
|
+
* @security API keys should be treated as sensitive credentials.
|
|
151
|
+
*/
|
|
152
|
+
export interface HeliusProviderConfig extends ProviderConfig {
|
|
153
|
+
/**
|
|
154
|
+
* Helius API key (required)
|
|
155
|
+
*
|
|
156
|
+
* @security Treat as sensitive credential. Use environment variables.
|
|
157
|
+
* Never commit to source control or log in error messages.
|
|
158
|
+
* The SDK masks this key in error messages automatically.
|
|
159
|
+
*/
|
|
160
|
+
apiKey: string
|
|
161
|
+
/** Solana cluster (default: mainnet-beta) */
|
|
162
|
+
cluster?: 'mainnet-beta' | 'devnet'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Helius RPC Provider implementation
|
|
167
|
+
*
|
|
168
|
+
* Uses Helius DAS API for efficient token queries.
|
|
169
|
+
* Recommended for production deployments.
|
|
170
|
+
*/
|
|
171
|
+
export class HeliusProvider implements SolanaRPCProvider {
|
|
172
|
+
readonly name = 'helius'
|
|
173
|
+
private readonly apiKey: string
|
|
174
|
+
private readonly cluster: 'mainnet-beta' | 'devnet'
|
|
175
|
+
private readonly rpcUrl: string
|
|
176
|
+
private readonly restUrl: string
|
|
177
|
+
|
|
178
|
+
constructor(config: HeliusProviderConfig) {
|
|
179
|
+
// Validate API key
|
|
180
|
+
if (!config.apiKey) {
|
|
181
|
+
throw new ValidationError(
|
|
182
|
+
'Helius API key is required. Get one at https://dev.helius.xyz',
|
|
183
|
+
'apiKey'
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Validate API key format (basic check - Helius keys are UUIDs or alphanumeric)
|
|
188
|
+
if (typeof config.apiKey !== 'string' || config.apiKey.length < HELIUS_API_KEY_MIN_LENGTH) {
|
|
189
|
+
throw new ValidationError(
|
|
190
|
+
'Invalid Helius API key format',
|
|
191
|
+
'apiKey'
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.apiKey = config.apiKey
|
|
196
|
+
this.cluster = config.cluster ?? 'mainnet-beta'
|
|
197
|
+
|
|
198
|
+
// RPC endpoint for DAS API (no API key in URL - use header instead)
|
|
199
|
+
// H-1 FIX: API key moved from URL query parameter to Authorization header
|
|
200
|
+
this.rpcUrl = this.cluster === 'devnet'
|
|
201
|
+
? 'https://devnet.helius-rpc.com'
|
|
202
|
+
: 'https://mainnet.helius-rpc.com'
|
|
203
|
+
|
|
204
|
+
// REST endpoint for balances API
|
|
205
|
+
this.restUrl = this.cluster === 'devnet'
|
|
206
|
+
? 'https://api-devnet.helius.xyz/v0'
|
|
207
|
+
: 'https://api.helius.xyz/v0'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get all token assets owned by an address using DAS API
|
|
212
|
+
*
|
|
213
|
+
* Uses getAssetsByOwner for comprehensive asset information including
|
|
214
|
+
* NFTs and fungible tokens with metadata.
|
|
215
|
+
*/
|
|
216
|
+
async getAssetsByOwner(owner: string): Promise<TokenAsset[]> {
|
|
217
|
+
// Validate owner address
|
|
218
|
+
if (!owner || typeof owner !== 'string') {
|
|
219
|
+
throw new ValidationError('owner address is required', 'owner')
|
|
220
|
+
}
|
|
221
|
+
// Basic Solana address validation (32-44 chars, base58)
|
|
222
|
+
if (owner.length < SOLANA_ADDRESS_MIN_LENGTH || owner.length > SOLANA_ADDRESS_MAX_LENGTH) {
|
|
223
|
+
throw new ValidationError('invalid Solana address format', 'owner')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const assets: TokenAsset[] = []
|
|
227
|
+
let page = 1
|
|
228
|
+
const limit = HELIUS_DAS_PAGE_LIMIT
|
|
229
|
+
let hasMore = true
|
|
230
|
+
|
|
231
|
+
while (hasMore) {
|
|
232
|
+
const response = await fetchWithTimeout(this.rpcUrl, {
|
|
233
|
+
method: 'POST',
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': 'application/json',
|
|
236
|
+
// H-1 FIX: Use Authorization header instead of URL query parameter
|
|
237
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
238
|
+
},
|
|
239
|
+
body: JSON.stringify({
|
|
240
|
+
jsonrpc: '2.0',
|
|
241
|
+
id: `sip-${Date.now()}`,
|
|
242
|
+
method: 'getAssetsByOwner',
|
|
243
|
+
params: {
|
|
244
|
+
ownerAddress: owner,
|
|
245
|
+
page,
|
|
246
|
+
limit,
|
|
247
|
+
displayOptions: {
|
|
248
|
+
showFungible: true,
|
|
249
|
+
showNativeBalance: false,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
}),
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
// H-2 FIX: Never include API key in error messages, sanitize URLs
|
|
257
|
+
throw new NetworkError(
|
|
258
|
+
`Helius API error: ${response.status} ${response.statusText} (key: ${maskApiKey(this.apiKey)})`,
|
|
259
|
+
undefined,
|
|
260
|
+
{ endpoint: sanitizeUrl(this.rpcUrl), statusCode: response.status }
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const data = (await response.json()) as HeliusDASResponse
|
|
265
|
+
|
|
266
|
+
// Handle JSON-RPC errors
|
|
267
|
+
if (data.error) {
|
|
268
|
+
throw new NetworkError(
|
|
269
|
+
`Helius RPC error: ${data.error.message} (code: ${data.error.code})`,
|
|
270
|
+
undefined,
|
|
271
|
+
{ endpoint: sanitizeUrl(this.rpcUrl) }
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (data.result?.items) {
|
|
276
|
+
for (const item of data.result.items) {
|
|
277
|
+
// Skip NFTs (interface !== 'FungibleToken' and 'FungibleAsset')
|
|
278
|
+
if (item.interface !== 'FungibleToken' && item.interface !== 'FungibleAsset') {
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Extract token info
|
|
283
|
+
const tokenInfo = item.token_info
|
|
284
|
+
if (!tokenInfo?.balance) continue
|
|
285
|
+
|
|
286
|
+
// Convert balance to BigInt, handling both string and number types
|
|
287
|
+
// Always use string parsing for BigInt to avoid precision loss
|
|
288
|
+
let balanceValue: bigint
|
|
289
|
+
if (typeof tokenInfo.balance === 'string') {
|
|
290
|
+
balanceValue = BigInt(tokenInfo.balance)
|
|
291
|
+
} else {
|
|
292
|
+
// For numbers, convert to string first to avoid precision loss
|
|
293
|
+
balanceValue = BigInt(Math.floor(tokenInfo.balance).toString())
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
assets.push({
|
|
297
|
+
mint: item.id,
|
|
298
|
+
amount: balanceValue,
|
|
299
|
+
decimals: tokenInfo.decimals ?? 0,
|
|
300
|
+
symbol: tokenInfo.symbol ?? item.content?.metadata?.symbol,
|
|
301
|
+
name: item.content?.metadata?.name,
|
|
302
|
+
logoUri: item.content?.links?.image,
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check if there are more pages
|
|
308
|
+
hasMore = data.result?.items?.length === limit
|
|
309
|
+
page++
|
|
310
|
+
|
|
311
|
+
// Safety limit to prevent infinite loops
|
|
312
|
+
if (page > HELIUS_MAX_PAGES) {
|
|
313
|
+
break
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return assets
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Get token balance for a specific mint using Balances API
|
|
322
|
+
*
|
|
323
|
+
* More efficient than getAssetsByOwner when you only need one token's balance.
|
|
324
|
+
*/
|
|
325
|
+
async getTokenBalance(owner: string, mint: string): Promise<bigint> {
|
|
326
|
+
// Validate inputs
|
|
327
|
+
if (!owner || typeof owner !== 'string') {
|
|
328
|
+
throw new ValidationError('owner address is required', 'owner')
|
|
329
|
+
}
|
|
330
|
+
if (!mint || typeof mint !== 'string') {
|
|
331
|
+
throw new ValidationError('mint address is required', 'mint')
|
|
332
|
+
}
|
|
333
|
+
// Validate address format
|
|
334
|
+
if (owner.length < SOLANA_ADDRESS_MIN_LENGTH || owner.length > SOLANA_ADDRESS_MAX_LENGTH) {
|
|
335
|
+
throw new ValidationError('invalid owner address format', 'owner')
|
|
336
|
+
}
|
|
337
|
+
if (mint.length < SOLANA_ADDRESS_MIN_LENGTH || mint.length > SOLANA_ADDRESS_MAX_LENGTH) {
|
|
338
|
+
throw new ValidationError('invalid mint address format', 'mint')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const url = `${this.restUrl}/addresses/${owner}/balances`
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const response = await fetchWithTimeout(url, {
|
|
345
|
+
headers: {
|
|
346
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
if (!response.ok) {
|
|
351
|
+
// Only fallback for specific recoverable errors (404, 503)
|
|
352
|
+
// Don't fallback for auth errors (401, 403) or client errors (400)
|
|
353
|
+
if (response.status === 404 || response.status === 503) {
|
|
354
|
+
return this.getTokenBalanceFallback(owner, mint)
|
|
355
|
+
}
|
|
356
|
+
// For other errors, throw rather than silently fallback
|
|
357
|
+
// H-2 FIX: Sanitize URL to prevent credential exposure
|
|
358
|
+
throw new NetworkError(
|
|
359
|
+
`Helius Balances API error: ${response.status}`,
|
|
360
|
+
undefined,
|
|
361
|
+
{ endpoint: sanitizeUrl(url), statusCode: response.status }
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const data = (await response.json()) as HeliusBalancesResponse
|
|
366
|
+
|
|
367
|
+
const token = data.tokens?.find((t) => t.mint === mint)
|
|
368
|
+
return token ? BigInt(token.amount) : 0n
|
|
369
|
+
} catch (error) {
|
|
370
|
+
// Only fallback for network/timeout errors, not all errors
|
|
371
|
+
if (error instanceof NetworkError && error.message.includes('timeout')) {
|
|
372
|
+
return this.getTokenBalanceFallback(owner, mint)
|
|
373
|
+
}
|
|
374
|
+
// Re-throw validation and other errors
|
|
375
|
+
throw error
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Fallback method to DAS API for token balance
|
|
381
|
+
* Only called for specific recoverable errors
|
|
382
|
+
* @internal
|
|
383
|
+
*/
|
|
384
|
+
private async getTokenBalanceFallback(owner: string, mint: string): Promise<bigint> {
|
|
385
|
+
const assets = await this.getAssetsByOwner(owner)
|
|
386
|
+
const asset = assets.find((a) => a.mint === mint)
|
|
387
|
+
return asset?.amount ?? 0n
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Check if provider supports real-time subscriptions
|
|
392
|
+
*
|
|
393
|
+
* Helius supports webhooks for real-time notifications,
|
|
394
|
+
* but that requires server-side setup. Client-side subscriptions
|
|
395
|
+
* are not directly supported.
|
|
396
|
+
*/
|
|
397
|
+
supportsSubscriptions(): boolean {
|
|
398
|
+
// Helius has webhooks but not client-side subscriptions
|
|
399
|
+
// Return false for now, can be enhanced with webhook integration later
|
|
400
|
+
return false
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana RPC Providers
|
|
3
|
+
*
|
|
4
|
+
* SIP is RPC-provider-agnostic — developers choose their preferred provider.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { createProvider, scanForPayments } from '@sip-protocol/sdk'
|
|
9
|
+
*
|
|
10
|
+
* // Helius — efficient DAS queries (recommended for production)
|
|
11
|
+
* const helius = createProvider('helius', {
|
|
12
|
+
* apiKey: process.env.HELIUS_API_KEY!
|
|
13
|
+
* })
|
|
14
|
+
*
|
|
15
|
+
* // Generic — standard RPC, no API key needed
|
|
16
|
+
* const generic = createProvider('generic', {
|
|
17
|
+
* endpoint: 'https://api.devnet.solana.com'
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // Same API, different backends
|
|
21
|
+
* const payments = await scanForPayments({
|
|
22
|
+
* provider: helius,
|
|
23
|
+
* viewingPrivateKey,
|
|
24
|
+
* spendingPublicKey,
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// Interface and factory
|
|
32
|
+
export {
|
|
33
|
+
createProvider,
|
|
34
|
+
type SolanaRPCProvider,
|
|
35
|
+
type TokenAsset,
|
|
36
|
+
type ProviderConfig,
|
|
37
|
+
type ProviderType,
|
|
38
|
+
type GenericProviderConfig,
|
|
39
|
+
} from './interface'
|
|
40
|
+
|
|
41
|
+
// Provider implementations
|
|
42
|
+
export { HeliusProvider, type HeliusProviderConfig } from './helius'
|
|
43
|
+
export { GenericProvider } from './generic'
|
|
44
|
+
export { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
|
|
45
|
+
export { TritonProvider, type TritonProviderConfig } from './triton'
|
|
46
|
+
|
|
47
|
+
// Webhook handler for real-time scanning
|
|
48
|
+
export {
|
|
49
|
+
createWebhookHandler,
|
|
50
|
+
processWebhookTransaction,
|
|
51
|
+
verifyWebhookSignature,
|
|
52
|
+
verifyAuthToken,
|
|
53
|
+
type HeliusWebhookTransaction,
|
|
54
|
+
type HeliusEnhancedTransaction,
|
|
55
|
+
type HeliusWebhookPayload,
|
|
56
|
+
type WebhookHandlerConfig,
|
|
57
|
+
type WebhookProcessResult,
|
|
58
|
+
type WebhookRequest,
|
|
59
|
+
type WebhookHandler,
|
|
60
|
+
} from './webhook'
|
|
61
|
+
|
|
62
|
+
// Enhanced Transactions API for human-readable tx data
|
|
63
|
+
export {
|
|
64
|
+
HeliusEnhanced,
|
|
65
|
+
createHeliusEnhanced,
|
|
66
|
+
type HeliusEnhancedConfig,
|
|
67
|
+
} from './helius-enhanced'
|
|
68
|
+
|
|
69
|
+
// Enhanced Transactions types
|
|
70
|
+
export type {
|
|
71
|
+
EnhancedTransactionType,
|
|
72
|
+
NativeTransfer,
|
|
73
|
+
TokenTransfer,
|
|
74
|
+
NftTransfer,
|
|
75
|
+
SwapEvent,
|
|
76
|
+
EnhancedTransactionEvents,
|
|
77
|
+
EnhancedAccountData,
|
|
78
|
+
EnhancedTransaction,
|
|
79
|
+
ParseTransactionsOptions,
|
|
80
|
+
GetTransactionHistoryOptions,
|
|
81
|
+
PrivacyDisplayOptions,
|
|
82
|
+
SIPTransactionMetadata,
|
|
83
|
+
SIPEnhancedTransaction,
|
|
84
|
+
TransactionSummary,
|
|
85
|
+
} from './helius-enhanced-types'
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana RPC Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* SIP is RPC-provider-agnostic — developers choose their preferred provider.
|
|
5
|
+
* Each provider has unique moats we leverage through this unified interface.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createProvider, scanForPayments } from '@sip-protocol/sdk'
|
|
10
|
+
*
|
|
11
|
+
* // Helius — efficient DAS queries (recommended for production)
|
|
12
|
+
* const helius = createProvider('helius', { apiKey: process.env.HELIUS_API_KEY })
|
|
13
|
+
*
|
|
14
|
+
* // Generic — standard RPC, no API key needed
|
|
15
|
+
* const generic = createProvider('generic', { connection })
|
|
16
|
+
*
|
|
17
|
+
* // Same API, different backends
|
|
18
|
+
* const payments = await scanForPayments({
|
|
19
|
+
* provider: helius,
|
|
20
|
+
* viewingPrivateKey,
|
|
21
|
+
* spendingPublicKey,
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @packageDocumentation
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { HeliusProvider, type HeliusProviderConfig } from './helius'
|
|
29
|
+
import { GenericProvider } from './generic'
|
|
30
|
+
import { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
|
|
31
|
+
import { TritonProvider, type TritonProviderConfig } from './triton'
|
|
32
|
+
import { ValidationError, ErrorCode } from '../../../errors'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Token asset information returned by providers
|
|
36
|
+
*/
|
|
37
|
+
export interface TokenAsset {
|
|
38
|
+
/** SPL token mint address */
|
|
39
|
+
mint: string
|
|
40
|
+
/** Token amount in smallest units */
|
|
41
|
+
amount: bigint
|
|
42
|
+
/** Token decimals */
|
|
43
|
+
decimals: number
|
|
44
|
+
/** Token symbol (e.g., 'USDC') */
|
|
45
|
+
symbol?: string
|
|
46
|
+
/** Token name (e.g., 'USD Coin') */
|
|
47
|
+
name?: string
|
|
48
|
+
/** Token logo URI */
|
|
49
|
+
logoUri?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Configuration for RPC providers
|
|
54
|
+
*/
|
|
55
|
+
export interface ProviderConfig {
|
|
56
|
+
/** API key for premium providers (Helius, QuickNode) */
|
|
57
|
+
apiKey?: string
|
|
58
|
+
/** Custom RPC endpoint */
|
|
59
|
+
endpoint?: string
|
|
60
|
+
/** Solana cluster */
|
|
61
|
+
cluster?: 'mainnet-beta' | 'devnet' | 'testnet'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Unified interface for Solana RPC providers
|
|
66
|
+
*
|
|
67
|
+
* All provider adapters must implement this interface to ensure
|
|
68
|
+
* consistent behavior across different RPC backends.
|
|
69
|
+
*/
|
|
70
|
+
export interface SolanaRPCProvider {
|
|
71
|
+
/** Provider name for logging/debugging */
|
|
72
|
+
readonly name: string
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get all token assets owned by an address
|
|
76
|
+
*
|
|
77
|
+
* @param owner - Solana address (base58)
|
|
78
|
+
* @returns Array of token assets with balances
|
|
79
|
+
*/
|
|
80
|
+
getAssetsByOwner(owner: string): Promise<TokenAsset[]>
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get token balance for a specific mint
|
|
84
|
+
*
|
|
85
|
+
* @param owner - Solana address (base58)
|
|
86
|
+
* @param mint - SPL token mint address (base58)
|
|
87
|
+
* @returns Token balance in smallest units, 0n if no balance
|
|
88
|
+
*/
|
|
89
|
+
getTokenBalance(owner: string, mint: string): Promise<bigint>
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if provider supports real-time subscriptions
|
|
93
|
+
*
|
|
94
|
+
* @returns true if subscribeToTransfers is available
|
|
95
|
+
*/
|
|
96
|
+
supportsSubscriptions(): boolean
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Subscribe to token transfers for an address (optional)
|
|
100
|
+
*
|
|
101
|
+
* Only available if supportsSubscriptions() returns true.
|
|
102
|
+
*
|
|
103
|
+
* @param address - Solana address to watch
|
|
104
|
+
* @param callback - Called when a transfer is detected
|
|
105
|
+
* @returns Unsubscribe function
|
|
106
|
+
*/
|
|
107
|
+
subscribeToTransfers?(
|
|
108
|
+
address: string,
|
|
109
|
+
callback: (asset: TokenAsset) => void
|
|
110
|
+
): Promise<() => void>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Provider type for factory function
|
|
115
|
+
*/
|
|
116
|
+
export type ProviderType = 'helius' | 'quicknode' | 'triton' | 'generic'
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Extended config for generic provider that accepts a Connection
|
|
120
|
+
*/
|
|
121
|
+
export interface GenericProviderConfig extends ProviderConfig {
|
|
122
|
+
/** Existing Solana Connection object */
|
|
123
|
+
connection?: unknown // Typed as unknown to avoid @solana/web3.js dependency in interface
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create an RPC provider instance
|
|
128
|
+
*
|
|
129
|
+
* @param type - Provider type ('helius', 'quicknode', 'triton', 'generic')
|
|
130
|
+
* @param config - Provider configuration
|
|
131
|
+
* @returns Configured provider instance
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* // Helius with DAS API (recommended for production)
|
|
136
|
+
* const helius = createProvider('helius', {
|
|
137
|
+
* apiKey: process.env.HELIUS_API_KEY,
|
|
138
|
+
* cluster: 'devnet'
|
|
139
|
+
* })
|
|
140
|
+
*
|
|
141
|
+
* // QuickNode with Yellowstone gRPC (real-time subscriptions)
|
|
142
|
+
* const quicknode = createProvider('quicknode', {
|
|
143
|
+
* endpoint: process.env.QUICKNODE_ENDPOINT
|
|
144
|
+
* })
|
|
145
|
+
*
|
|
146
|
+
* // Triton with Dragon's Mouth gRPC (ultra-low latency)
|
|
147
|
+
* const triton = createProvider('triton', {
|
|
148
|
+
* xToken: process.env.TRITON_TOKEN
|
|
149
|
+
* })
|
|
150
|
+
*
|
|
151
|
+
* // Generic with existing connection
|
|
152
|
+
* const generic = createProvider('generic', { connection })
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function createProvider(
|
|
156
|
+
type: 'helius',
|
|
157
|
+
config: ProviderConfig & { apiKey: string }
|
|
158
|
+
): SolanaRPCProvider
|
|
159
|
+
export function createProvider(
|
|
160
|
+
type: 'quicknode',
|
|
161
|
+
config: QuickNodeProviderConfig
|
|
162
|
+
): SolanaRPCProvider
|
|
163
|
+
export function createProvider(
|
|
164
|
+
type: 'triton',
|
|
165
|
+
config: TritonProviderConfig
|
|
166
|
+
): SolanaRPCProvider
|
|
167
|
+
export function createProvider(
|
|
168
|
+
type: 'generic',
|
|
169
|
+
config: GenericProviderConfig
|
|
170
|
+
): SolanaRPCProvider
|
|
171
|
+
export function createProvider(
|
|
172
|
+
type: ProviderType,
|
|
173
|
+
config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
|
|
174
|
+
): SolanaRPCProvider
|
|
175
|
+
export function createProvider(
|
|
176
|
+
type: ProviderType,
|
|
177
|
+
config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
|
|
178
|
+
): SolanaRPCProvider {
|
|
179
|
+
// Validate config before type casting
|
|
180
|
+
if (!config || typeof config !== 'object') {
|
|
181
|
+
throw new ValidationError('Provider config is required', 'config')
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
switch (type) {
|
|
185
|
+
case 'helius': {
|
|
186
|
+
// Validate required fields for HeliusProvider
|
|
187
|
+
const heliusConfig = config as HeliusProviderConfig
|
|
188
|
+
if (!heliusConfig.apiKey || typeof heliusConfig.apiKey !== 'string') {
|
|
189
|
+
throw new ValidationError(
|
|
190
|
+
'Helius provider requires an API key',
|
|
191
|
+
'apiKey'
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
if (heliusConfig.cluster && !['mainnet-beta', 'devnet'].includes(heliusConfig.cluster)) {
|
|
195
|
+
throw new ValidationError(
|
|
196
|
+
'Invalid cluster. Must be "mainnet-beta" or "devnet"',
|
|
197
|
+
'cluster'
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
return new HeliusProvider(heliusConfig)
|
|
201
|
+
}
|
|
202
|
+
case 'quicknode':
|
|
203
|
+
return new QuickNodeProvider(config as QuickNodeProviderConfig)
|
|
204
|
+
case 'triton':
|
|
205
|
+
return new TritonProvider(config as TritonProviderConfig)
|
|
206
|
+
case 'generic': {
|
|
207
|
+
// Validate GenericProvider config
|
|
208
|
+
const genericConfig = config as GenericProviderConfig
|
|
209
|
+
// Must have either connection, endpoint, or cluster
|
|
210
|
+
if (!genericConfig.connection && !genericConfig.endpoint && !genericConfig.cluster) {
|
|
211
|
+
throw new ValidationError(
|
|
212
|
+
'Generic provider requires either connection, endpoint, or cluster',
|
|
213
|
+
'config'
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
return new GenericProvider(genericConfig)
|
|
217
|
+
}
|
|
218
|
+
default:
|
|
219
|
+
throw new ValidationError(`unknown provider type: ${type}`, 'type', undefined, ErrorCode.INVALID_INPUT)
|
|
220
|
+
}
|
|
221
|
+
}
|