@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
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triton RPC Provider
|
|
3
|
+
*
|
|
4
|
+
* Leverages Triton's high-performance Solana RPC for queries and
|
|
5
|
+
* Dragon's Mouth gRPC (Yellowstone) for real-time streaming.
|
|
6
|
+
*
|
|
7
|
+
* Triton is known for ultra-low latency (~400ms advantage over WebSocket)
|
|
8
|
+
* and multi-region failover for high availability.
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.triton.one/chains/solana
|
|
11
|
+
* @see https://docs.triton.one/project-yellowstone/dragons-mouth-grpc-subscriptions
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { TritonProvider } from '@sip-protocol/sdk'
|
|
16
|
+
*
|
|
17
|
+
* const triton = new TritonProvider({
|
|
18
|
+
* xToken: process.env.TRITON_TOKEN!,
|
|
19
|
+
* cluster: 'mainnet-beta'
|
|
20
|
+
* })
|
|
21
|
+
*
|
|
22
|
+
* // Query assets (standard RPC)
|
|
23
|
+
* const assets = await triton.getAssetsByOwner('7xK9...')
|
|
24
|
+
*
|
|
25
|
+
* // Real-time subscriptions (Dragon's Mouth gRPC)
|
|
26
|
+
* if (triton.supportsSubscriptions()) {
|
|
27
|
+
* const unsubscribe = await triton.subscribeToTransfers('7xK9...', (asset) => {
|
|
28
|
+
* console.log('Transfer received:', asset)
|
|
29
|
+
* })
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
Connection,
|
|
36
|
+
PublicKey,
|
|
37
|
+
} from '@solana/web3.js'
|
|
38
|
+
import {
|
|
39
|
+
TOKEN_PROGRAM_ID,
|
|
40
|
+
getAssociatedTokenAddress,
|
|
41
|
+
getAccount,
|
|
42
|
+
getMint,
|
|
43
|
+
} from '@solana/spl-token'
|
|
44
|
+
import Client, {
|
|
45
|
+
CommitmentLevel,
|
|
46
|
+
type SubscribeRequest,
|
|
47
|
+
type SubscribeUpdate,
|
|
48
|
+
} from '@triton-one/yellowstone-grpc'
|
|
49
|
+
import type { ClientDuplexStream } from '@grpc/grpc-js'
|
|
50
|
+
import { base58 } from '@scure/base'
|
|
51
|
+
import type { SolanaRPCProvider, TokenAsset, ProviderConfig } from './interface'
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Type alias for Yellowstone gRPC subscription stream
|
|
55
|
+
*/
|
|
56
|
+
type GrpcSubscriptionStream = ClientDuplexStream<SubscribeRequest, SubscribeUpdate>
|
|
57
|
+
|
|
58
|
+
import { ValidationError, ErrorCode } from '../../../errors'
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Triton provider configuration
|
|
62
|
+
*/
|
|
63
|
+
export interface TritonProviderConfig extends ProviderConfig {
|
|
64
|
+
/**
|
|
65
|
+
* Triton x-token for authentication
|
|
66
|
+
*/
|
|
67
|
+
xToken: string
|
|
68
|
+
/**
|
|
69
|
+
* Custom RPC endpoint (optional)
|
|
70
|
+
* Default: https://mainnet.rpcpool.com or https://devnet.rpcpool.com
|
|
71
|
+
*/
|
|
72
|
+
endpoint?: string
|
|
73
|
+
/**
|
|
74
|
+
* Custom gRPC endpoint (optional)
|
|
75
|
+
* Default: https://grpc.rpcpool.com:443
|
|
76
|
+
*/
|
|
77
|
+
grpcEndpoint?: string
|
|
78
|
+
/**
|
|
79
|
+
* Solana cluster (default: mainnet-beta)
|
|
80
|
+
*/
|
|
81
|
+
cluster?: 'mainnet-beta' | 'devnet'
|
|
82
|
+
/**
|
|
83
|
+
* Enable Dragon's Mouth gRPC for real-time subscriptions
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
enableGrpc?: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Default Triton endpoints by cluster
|
|
91
|
+
*/
|
|
92
|
+
const TRITON_RPC_ENDPOINTS: Record<string, string> = {
|
|
93
|
+
'mainnet-beta': 'https://mainnet.rpcpool.com',
|
|
94
|
+
devnet: 'https://devnet.rpcpool.com',
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Default Triton gRPC endpoints by cluster
|
|
99
|
+
*/
|
|
100
|
+
const TRITON_GRPC_ENDPOINTS: Record<string, string> = {
|
|
101
|
+
'mainnet-beta': 'https://grpc.rpcpool.com:443',
|
|
102
|
+
devnet: 'https://grpc-devnet.rpcpool.com:443',
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Validate a Solana address (base58)
|
|
107
|
+
* @throws Error if address is invalid
|
|
108
|
+
*/
|
|
109
|
+
function validateSolanaAddress(address: string, paramName: string): PublicKey {
|
|
110
|
+
try {
|
|
111
|
+
return new PublicKey(address)
|
|
112
|
+
} catch {
|
|
113
|
+
throw new ValidationError('invalid Solana address format', paramName, undefined, ErrorCode.INVALID_ADDRESS)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Triton RPC Provider implementation
|
|
119
|
+
*
|
|
120
|
+
* Uses Triton's high-performance RPC for queries and Dragon's Mouth
|
|
121
|
+
* gRPC for real-time subscriptions. Known for ultra-low latency and
|
|
122
|
+
* high availability, ideal for DeFi and trading applications.
|
|
123
|
+
*/
|
|
124
|
+
export class TritonProvider implements SolanaRPCProvider {
|
|
125
|
+
readonly name = 'triton'
|
|
126
|
+
private connection: Connection
|
|
127
|
+
private grpcEndpoint: string
|
|
128
|
+
private xToken: string
|
|
129
|
+
private grpcEnabled: boolean
|
|
130
|
+
private grpcClient: Client | null = null
|
|
131
|
+
/** Active gRPC subscription streams */
|
|
132
|
+
private activeStreams: Set<GrpcSubscriptionStream> = new Set()
|
|
133
|
+
/** Cache for mint decimals to avoid repeated RPC calls */
|
|
134
|
+
private mintDecimalsCache: Map<string, number> = new Map()
|
|
135
|
+
|
|
136
|
+
constructor(config: TritonProviderConfig) {
|
|
137
|
+
if (!config.xToken) {
|
|
138
|
+
throw new ValidationError(
|
|
139
|
+
'x-token is required. Get one at https://triton.one/',
|
|
140
|
+
'xToken',
|
|
141
|
+
undefined,
|
|
142
|
+
ErrorCode.MISSING_REQUIRED
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const cluster = config.cluster ?? 'mainnet-beta'
|
|
147
|
+
const rpcEndpoint = config.endpoint ?? TRITON_RPC_ENDPOINTS[cluster]
|
|
148
|
+
|
|
149
|
+
// Append x-token to RPC endpoint
|
|
150
|
+
const normalizedEndpoint = rpcEndpoint.replace(/\/$/, '') // Remove trailing slash
|
|
151
|
+
const rpcUrl = normalizedEndpoint.includes('?')
|
|
152
|
+
? `${normalizedEndpoint}&x-token=${config.xToken}`
|
|
153
|
+
: `${normalizedEndpoint}/${config.xToken}`
|
|
154
|
+
|
|
155
|
+
this.connection = new Connection(rpcUrl, 'confirmed')
|
|
156
|
+
this.grpcEndpoint = config.grpcEndpoint ?? TRITON_GRPC_ENDPOINTS[cluster]
|
|
157
|
+
this.xToken = config.xToken
|
|
158
|
+
this.grpcEnabled = config.enableGrpc !== false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get all token assets owned by an address using standard RPC
|
|
163
|
+
*
|
|
164
|
+
* Uses getParsedTokenAccountsByOwner for comprehensive asset information.
|
|
165
|
+
*/
|
|
166
|
+
async getAssetsByOwner(owner: string): Promise<TokenAsset[]> {
|
|
167
|
+
const ownerPubkey = validateSolanaAddress(owner, 'owner')
|
|
168
|
+
|
|
169
|
+
const accounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
170
|
+
ownerPubkey,
|
|
171
|
+
{ programId: TOKEN_PROGRAM_ID }
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
const assets: TokenAsset[] = []
|
|
175
|
+
|
|
176
|
+
for (const { account } of accounts.value) {
|
|
177
|
+
const parsed = account.data.parsed
|
|
178
|
+
if (parsed.type !== 'account') continue
|
|
179
|
+
|
|
180
|
+
const info = parsed.info
|
|
181
|
+
const amount = BigInt(info.tokenAmount.amount)
|
|
182
|
+
|
|
183
|
+
// Skip zero balances
|
|
184
|
+
if (amount === 0n) continue
|
|
185
|
+
|
|
186
|
+
assets.push({
|
|
187
|
+
mint: info.mint,
|
|
188
|
+
amount,
|
|
189
|
+
decimals: info.tokenAmount.decimals,
|
|
190
|
+
// Standard RPC doesn't provide symbol/name, those need metadata lookup
|
|
191
|
+
symbol: undefined,
|
|
192
|
+
name: undefined,
|
|
193
|
+
logoUri: undefined,
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return assets
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get token balance for a specific mint
|
|
202
|
+
*
|
|
203
|
+
* Uses getAccount on the associated token address.
|
|
204
|
+
*/
|
|
205
|
+
async getTokenBalance(owner: string, mint: string): Promise<bigint> {
|
|
206
|
+
const ownerPubkey = validateSolanaAddress(owner, 'owner')
|
|
207
|
+
const mintPubkey = validateSolanaAddress(mint, 'mint')
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const ata = await getAssociatedTokenAddress(
|
|
211
|
+
mintPubkey,
|
|
212
|
+
ownerPubkey,
|
|
213
|
+
true // allowOwnerOffCurve for PDAs
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
const account = await getAccount(this.connection, ata)
|
|
217
|
+
return account.amount
|
|
218
|
+
} catch {
|
|
219
|
+
// Account doesn't exist or other RPC error
|
|
220
|
+
return 0n
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Check if provider supports real-time subscriptions
|
|
226
|
+
*
|
|
227
|
+
* Triton supports Dragon's Mouth gRPC (Yellowstone) for
|
|
228
|
+
* real-time streaming with ~400ms latency advantage.
|
|
229
|
+
*/
|
|
230
|
+
supportsSubscriptions(): boolean {
|
|
231
|
+
return this.grpcEnabled
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get token decimals from mint metadata with caching
|
|
236
|
+
*
|
|
237
|
+
* @param mint - Token mint address (base58)
|
|
238
|
+
* @returns Token decimals (0-18), defaults to 9 if fetch fails
|
|
239
|
+
*/
|
|
240
|
+
private async getMintDecimals(mint: string): Promise<number> {
|
|
241
|
+
// Check cache first
|
|
242
|
+
const cached = this.mintDecimalsCache.get(mint)
|
|
243
|
+
if (cached !== undefined) {
|
|
244
|
+
return cached
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const mintPubkey = new PublicKey(mint)
|
|
249
|
+
const mintInfo = await getMint(this.connection, mintPubkey)
|
|
250
|
+
const decimals = mintInfo.decimals
|
|
251
|
+
this.mintDecimalsCache.set(mint, decimals)
|
|
252
|
+
return decimals
|
|
253
|
+
} catch {
|
|
254
|
+
// Default to 9 (SOL decimals) if fetch fails
|
|
255
|
+
// This is a safe fallback since most Solana tokens use 9 decimals
|
|
256
|
+
return 9
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Initialize gRPC client lazily
|
|
262
|
+
*/
|
|
263
|
+
private async getGrpcClient(): Promise<Client> {
|
|
264
|
+
if (!this.grpcEnabled) {
|
|
265
|
+
throw new ValidationError(
|
|
266
|
+
'gRPC subscriptions are disabled. Set enableGrpc: true in config',
|
|
267
|
+
'enableGrpc',
|
|
268
|
+
undefined,
|
|
269
|
+
ErrorCode.INVALID_INPUT
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!this.grpcClient) {
|
|
274
|
+
// Triton uses x-token for gRPC authentication
|
|
275
|
+
this.grpcClient = new Client(this.grpcEndpoint, this.xToken, {})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return this.grpcClient
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Subscribe to token transfers for an address
|
|
283
|
+
*
|
|
284
|
+
* Uses Dragon's Mouth gRPC to receive real-time notifications when
|
|
285
|
+
* tokens are transferred to the specified address.
|
|
286
|
+
*
|
|
287
|
+
* Dragon's Mouth provides ~400ms latency advantage over WebSocket,
|
|
288
|
+
* making it ideal for DeFi and trading applications.
|
|
289
|
+
*
|
|
290
|
+
* @param address - Solana address to watch for incoming transfers
|
|
291
|
+
* @param callback - Called when a transfer is detected
|
|
292
|
+
* @returns Unsubscribe function
|
|
293
|
+
*/
|
|
294
|
+
async subscribeToTransfers(
|
|
295
|
+
address: string,
|
|
296
|
+
callback: (asset: TokenAsset) => void
|
|
297
|
+
): Promise<() => void> {
|
|
298
|
+
validateSolanaAddress(address, 'address')
|
|
299
|
+
|
|
300
|
+
const client = await this.getGrpcClient()
|
|
301
|
+
const stream = await client.subscribe()
|
|
302
|
+
|
|
303
|
+
this.activeStreams.add(stream)
|
|
304
|
+
|
|
305
|
+
// Handle incoming data
|
|
306
|
+
stream.on('data', (update) => {
|
|
307
|
+
// Check for account update
|
|
308
|
+
if (update.account?.account) {
|
|
309
|
+
const accountData = update.account.account
|
|
310
|
+
|
|
311
|
+
// Only process token account updates
|
|
312
|
+
if (accountData.owner?.toString() === TOKEN_PROGRAM_ID.toBase58()) {
|
|
313
|
+
try {
|
|
314
|
+
// Parse SPL Token account data
|
|
315
|
+
// Token account structure: mint (32) + owner (32) + amount (8) + ...
|
|
316
|
+
const data = accountData.data
|
|
317
|
+
if (data && data.length >= 72) {
|
|
318
|
+
const mint = new PublicKey(data.slice(0, 32)).toBase58()
|
|
319
|
+
const amount = BigInt(
|
|
320
|
+
'0x' + Buffer.from(data.slice(64, 72)).reverse().toString('hex')
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if (amount > 0n) {
|
|
324
|
+
// Fetch decimals asynchronously and invoke callback
|
|
325
|
+
this.getMintDecimals(mint).then((decimals) => {
|
|
326
|
+
callback({
|
|
327
|
+
mint,
|
|
328
|
+
amount,
|
|
329
|
+
decimals,
|
|
330
|
+
symbol: undefined,
|
|
331
|
+
name: undefined,
|
|
332
|
+
logoUri: undefined,
|
|
333
|
+
})
|
|
334
|
+
}).catch(() => {
|
|
335
|
+
// Still invoke callback with default decimals on error
|
|
336
|
+
callback({
|
|
337
|
+
mint,
|
|
338
|
+
amount,
|
|
339
|
+
decimals: 9,
|
|
340
|
+
symbol: undefined,
|
|
341
|
+
name: undefined,
|
|
342
|
+
logoUri: undefined,
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
// Skip malformed account data
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
stream.on('error', (err) => {
|
|
355
|
+
console.error('[TritonProvider] gRPC stream error:', err.message)
|
|
356
|
+
this.activeStreams.delete(stream)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
stream.on('end', () => {
|
|
360
|
+
this.activeStreams.delete(stream)
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
// Create subscription request for token accounts owned by address
|
|
364
|
+
const request: SubscribeRequest = {
|
|
365
|
+
accounts: {
|
|
366
|
+
stealth: {
|
|
367
|
+
account: [],
|
|
368
|
+
owner: [TOKEN_PROGRAM_ID.toBase58()],
|
|
369
|
+
filters: [
|
|
370
|
+
{
|
|
371
|
+
memcmp: {
|
|
372
|
+
offset: '32', // Owner field offset in token account
|
|
373
|
+
bytes: new Uint8Array(base58.decode(address)),
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
commitment: CommitmentLevel.CONFIRMED,
|
|
380
|
+
accountsDataSlice: [],
|
|
381
|
+
slots: {},
|
|
382
|
+
transactions: {},
|
|
383
|
+
transactionsStatus: {},
|
|
384
|
+
blocks: {},
|
|
385
|
+
blocksMeta: {},
|
|
386
|
+
entry: {},
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Send subscription request
|
|
390
|
+
await new Promise<void>((resolve, reject) => {
|
|
391
|
+
stream.write(request, (err: Error | null | undefined) => {
|
|
392
|
+
if (err) {
|
|
393
|
+
reject(err)
|
|
394
|
+
} else {
|
|
395
|
+
resolve()
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
// Return unsubscribe function
|
|
401
|
+
return () => {
|
|
402
|
+
stream.end()
|
|
403
|
+
this.activeStreams.delete(stream)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Get the underlying Connection object
|
|
409
|
+
*
|
|
410
|
+
* Useful for advanced operations that need direct RPC access.
|
|
411
|
+
*/
|
|
412
|
+
getConnection(): Connection {
|
|
413
|
+
return this.connection
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Close all active subscriptions and cleanup resources
|
|
418
|
+
*/
|
|
419
|
+
async close(): Promise<void> {
|
|
420
|
+
for (const stream of this.activeStreams) {
|
|
421
|
+
stream.end()
|
|
422
|
+
}
|
|
423
|
+
this.activeStreams.clear()
|
|
424
|
+
this.grpcClient = null
|
|
425
|
+
}
|
|
426
|
+
}
|