@sip-protocol/sdk 0.7.3 → 0.8.0
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/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 +44 -11
- 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
|
@@ -21,6 +21,73 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
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
|
+
}
|
|
24
91
|
|
|
25
92
|
/**
|
|
26
93
|
* Helius API response types
|
|
@@ -79,9 +146,17 @@ interface HeliusBalancesResponse {
|
|
|
79
146
|
|
|
80
147
|
/**
|
|
81
148
|
* Helius provider configuration
|
|
149
|
+
*
|
|
150
|
+
* @security API keys should be treated as sensitive credentials.
|
|
82
151
|
*/
|
|
83
152
|
export interface HeliusProviderConfig extends ProviderConfig {
|
|
84
|
-
/**
|
|
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
|
+
*/
|
|
85
160
|
apiKey: string
|
|
86
161
|
/** Solana cluster (default: mainnet-beta) */
|
|
87
162
|
cluster?: 'mainnet-beta' | 'devnet'
|
|
@@ -95,28 +170,41 @@ export interface HeliusProviderConfig extends ProviderConfig {
|
|
|
95
170
|
*/
|
|
96
171
|
export class HeliusProvider implements SolanaRPCProvider {
|
|
97
172
|
readonly name = 'helius'
|
|
98
|
-
private apiKey: string
|
|
99
|
-
private cluster: 'mainnet-beta' | 'devnet'
|
|
100
|
-
private rpcUrl: string
|
|
101
|
-
private restUrl: string
|
|
173
|
+
private readonly apiKey: string
|
|
174
|
+
private readonly cluster: 'mainnet-beta' | 'devnet'
|
|
175
|
+
private readonly rpcUrl: string
|
|
176
|
+
private readonly restUrl: string
|
|
102
177
|
|
|
103
178
|
constructor(config: HeliusProviderConfig) {
|
|
179
|
+
// Validate API key
|
|
104
180
|
if (!config.apiKey) {
|
|
105
|
-
throw new
|
|
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
|
+
)
|
|
106
193
|
}
|
|
107
194
|
|
|
108
195
|
this.apiKey = config.apiKey
|
|
109
196
|
this.cluster = config.cluster ?? 'mainnet-beta'
|
|
110
197
|
|
|
111
|
-
// RPC endpoint for DAS API
|
|
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
|
|
112
200
|
this.rpcUrl = this.cluster === 'devnet'
|
|
113
|
-
?
|
|
114
|
-
:
|
|
201
|
+
? 'https://devnet.helius-rpc.com'
|
|
202
|
+
: 'https://mainnet.helius-rpc.com'
|
|
115
203
|
|
|
116
204
|
// REST endpoint for balances API
|
|
117
205
|
this.restUrl = this.cluster === 'devnet'
|
|
118
|
-
?
|
|
119
|
-
:
|
|
206
|
+
? 'https://api-devnet.helius.xyz/v0'
|
|
207
|
+
: 'https://api.helius.xyz/v0'
|
|
120
208
|
}
|
|
121
209
|
|
|
122
210
|
/**
|
|
@@ -126,15 +214,28 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
126
214
|
* NFTs and fungible tokens with metadata.
|
|
127
215
|
*/
|
|
128
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
|
+
|
|
129
226
|
const assets: TokenAsset[] = []
|
|
130
227
|
let page = 1
|
|
131
|
-
const limit =
|
|
228
|
+
const limit = HELIUS_DAS_PAGE_LIMIT
|
|
132
229
|
let hasMore = true
|
|
133
230
|
|
|
134
231
|
while (hasMore) {
|
|
135
|
-
const response = await
|
|
232
|
+
const response = await fetchWithTimeout(this.rpcUrl, {
|
|
136
233
|
method: 'POST',
|
|
137
|
-
headers: {
|
|
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
|
+
},
|
|
138
239
|
body: JSON.stringify({
|
|
139
240
|
jsonrpc: '2.0',
|
|
140
241
|
id: `sip-${Date.now()}`,
|
|
@@ -152,14 +253,23 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
152
253
|
})
|
|
153
254
|
|
|
154
255
|
if (!response.ok) {
|
|
155
|
-
|
|
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
|
+
)
|
|
156
262
|
}
|
|
157
263
|
|
|
158
264
|
const data = (await response.json()) as HeliusDASResponse
|
|
159
265
|
|
|
160
266
|
// Handle JSON-RPC errors
|
|
161
267
|
if (data.error) {
|
|
162
|
-
throw new
|
|
268
|
+
throw new NetworkError(
|
|
269
|
+
`Helius RPC error: ${data.error.message} (code: ${data.error.code})`,
|
|
270
|
+
undefined,
|
|
271
|
+
{ endpoint: sanitizeUrl(this.rpcUrl) }
|
|
272
|
+
)
|
|
163
273
|
}
|
|
164
274
|
|
|
165
275
|
if (data.result?.items) {
|
|
@@ -174,10 +284,14 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
174
284
|
if (!tokenInfo?.balance) continue
|
|
175
285
|
|
|
176
286
|
// Convert balance to BigInt, handling both string and number types
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
+
}
|
|
181
295
|
|
|
182
296
|
assets.push({
|
|
183
297
|
mint: item.id,
|
|
@@ -195,8 +309,7 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
195
309
|
page++
|
|
196
310
|
|
|
197
311
|
// Safety limit to prevent infinite loops
|
|
198
|
-
if (page >
|
|
199
|
-
console.warn('[HeliusProvider] Reached page limit (100), stopping pagination')
|
|
312
|
+
if (page > HELIUS_MAX_PAGES) {
|
|
200
313
|
break
|
|
201
314
|
}
|
|
202
315
|
}
|
|
@@ -210,16 +323,43 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
210
323
|
* More efficient than getAssetsByOwner when you only need one token's balance.
|
|
211
324
|
*/
|
|
212
325
|
async getTokenBalance(owner: string, mint: string): Promise<bigint> {
|
|
213
|
-
|
|
214
|
-
|
|
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
|
+
}
|
|
215
340
|
|
|
216
|
-
|
|
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
|
+
})
|
|
217
349
|
|
|
218
350
|
if (!response.ok) {
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
)
|
|
223
363
|
}
|
|
224
364
|
|
|
225
365
|
const data = (await response.json()) as HeliusBalancesResponse
|
|
@@ -227,13 +367,26 @@ export class HeliusProvider implements SolanaRPCProvider {
|
|
|
227
367
|
const token = data.tokens?.find((t) => t.mint === mint)
|
|
228
368
|
return token ? BigInt(token.amount) : 0n
|
|
229
369
|
} catch (error) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
234
376
|
}
|
|
235
377
|
}
|
|
236
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
|
+
|
|
237
390
|
/**
|
|
238
391
|
* Check if provider supports real-time subscriptions
|
|
239
392
|
*
|
|
@@ -41,14 +41,45 @@ export {
|
|
|
41
41
|
// Provider implementations
|
|
42
42
|
export { HeliusProvider, type HeliusProviderConfig } from './helius'
|
|
43
43
|
export { GenericProvider } from './generic'
|
|
44
|
+
export { QuickNodeProvider, type QuickNodeProviderConfig } from './quicknode'
|
|
45
|
+
export { TritonProvider, type TritonProviderConfig } from './triton'
|
|
44
46
|
|
|
45
47
|
// Webhook handler for real-time scanning
|
|
46
48
|
export {
|
|
47
49
|
createWebhookHandler,
|
|
48
50
|
processWebhookTransaction,
|
|
51
|
+
verifyWebhookSignature,
|
|
52
|
+
verifyAuthToken,
|
|
49
53
|
type HeliusWebhookTransaction,
|
|
50
54
|
type HeliusEnhancedTransaction,
|
|
51
55
|
type HeliusWebhookPayload,
|
|
52
56
|
type WebhookHandlerConfig,
|
|
53
57
|
type WebhookProcessResult,
|
|
58
|
+
type WebhookRequest,
|
|
59
|
+
type WebhookHandler,
|
|
54
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'
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
|
|
28
28
|
import { HeliusProvider, type HeliusProviderConfig } from './helius'
|
|
29
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'
|
|
30
33
|
|
|
31
34
|
/**
|
|
32
35
|
* Token asset information returned by providers
|
|
@@ -129,50 +132,90 @@ export interface GenericProviderConfig extends ProviderConfig {
|
|
|
129
132
|
*
|
|
130
133
|
* @example
|
|
131
134
|
* ```typescript
|
|
132
|
-
* // Helius with API
|
|
135
|
+
* // Helius with DAS API (recommended for production)
|
|
133
136
|
* const helius = createProvider('helius', {
|
|
134
137
|
* apiKey: process.env.HELIUS_API_KEY,
|
|
135
138
|
* cluster: 'devnet'
|
|
136
139
|
* })
|
|
137
140
|
*
|
|
138
|
-
* //
|
|
139
|
-
* const
|
|
141
|
+
* // QuickNode with Yellowstone gRPC (real-time subscriptions)
|
|
142
|
+
* const quicknode = createProvider('quicknode', {
|
|
143
|
+
* endpoint: process.env.QUICKNODE_ENDPOINT
|
|
144
|
+
* })
|
|
140
145
|
*
|
|
141
|
-
* //
|
|
142
|
-
* const
|
|
143
|
-
*
|
|
146
|
+
* // Triton with Dragon's Mouth gRPC (ultra-low latency)
|
|
147
|
+
* const triton = createProvider('triton', {
|
|
148
|
+
* xToken: process.env.TRITON_TOKEN
|
|
144
149
|
* })
|
|
150
|
+
*
|
|
151
|
+
* // Generic with existing connection
|
|
152
|
+
* const generic = createProvider('generic', { connection })
|
|
145
153
|
* ```
|
|
146
154
|
*/
|
|
147
155
|
export function createProvider(
|
|
148
156
|
type: 'helius',
|
|
149
157
|
config: ProviderConfig & { apiKey: string }
|
|
150
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
|
|
151
167
|
export function createProvider(
|
|
152
168
|
type: 'generic',
|
|
153
169
|
config: GenericProviderConfig
|
|
154
170
|
): SolanaRPCProvider
|
|
155
171
|
export function createProvider(
|
|
156
172
|
type: ProviderType,
|
|
157
|
-
config: ProviderConfig | GenericProviderConfig
|
|
173
|
+
config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
|
|
158
174
|
): SolanaRPCProvider
|
|
159
175
|
export function createProvider(
|
|
160
176
|
type: ProviderType,
|
|
161
|
-
config: ProviderConfig | GenericProviderConfig
|
|
177
|
+
config: ProviderConfig | GenericProviderConfig | QuickNodeProviderConfig | TritonProviderConfig
|
|
162
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
|
+
|
|
163
184
|
switch (type) {
|
|
164
|
-
case 'helius':
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
+
}
|
|
168
202
|
case 'quicknode':
|
|
203
|
+
return new QuickNodeProvider(config as QuickNodeProviderConfig)
|
|
169
204
|
case 'triton':
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
}
|
|
175
218
|
default:
|
|
176
|
-
throw new
|
|
219
|
+
throw new ValidationError(`unknown provider type: ${type}`, 'type', undefined, ErrorCode.INVALID_INPUT)
|
|
177
220
|
}
|
|
178
221
|
}
|