UNPKG

@stalkchain/grpc-pool

Version:

High-availability gRPC connection pooling module with active-active configuration, deduplication, and stale connection detection

215 lines • 8.93 kB
"use strict"; /** * okx-dex-subscriber.ts - OKX DEX Transaction Subscriber Example * * This example demonstrates subscribing to OKX DEX transactions and * processing the raw transaction data with proper debuffering for * human-readable output. * * Features: * - Subscribes to OKX DEX program transactions * - Debuffers transaction data for readability * - Logs transaction signatures, metadata, and first few lines of data * - Handles connection events and errors gracefully */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); require("dotenv/config"); const src_1 = require("../src"); const bs58_1 = __importDefault(require("bs58")); /** * Recursively converts Buffer objects and Uint8Arrays to base58 strings * @param {any} obj - Object to process * @returns {any} Processed object with readable strings */ function debufferMessage(obj) { if (obj === null || obj === undefined) { return obj; } // Handle Buffer objects (typical in older library versions or direct gRPC data) if (obj.type === 'Buffer' && Array.isArray(obj.data)) { return bs58_1.default.encode(new Uint8Array(obj.data)); } // Handle Uint8Array (more common with newer gRPC/protobuf versions) if (obj instanceof Uint8Array) { return bs58_1.default.encode(obj); } // Handle arrays recursively if (Array.isArray(obj)) { return obj.map((item) => debufferMessage(item)); } // Handle objects recursively if (typeof obj === 'object') { const debuffered = {}; for (const [key, value] of Object.entries(obj)) { debuffered[key] = debufferMessage(value); } return debuffered; } return obj; } // Configuration const SOLANA_GRPC_TOKEN = process.env.SOLANA_GRPC_TOKEN || ''; const SOLANA_GRPC_URL_MAIN = process.env.SOLANA_GRPC_URL_MAIN || 'https://grpc.solanatracker.io'; const SOLANA_GRPC_URL_SECONDARY = process.env.SOLANA_GRPC_URL_SECONDARY || 'https://grpc-us.solanatracker.io'; // OKX DEX Program ID (replace with actual OKX program if different) const OKX_DEX_PROGRAM = '6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma'; async function main() { console.log('šŸ¦ Starting OKX DEX Transaction Subscriber'); console.log('========================================='); console.log(`šŸ“ Target Program: ${OKX_DEX_PROGRAM}`); console.log(''); // Configure endpoints const poolConfig = { endpoints: [ { endpoint: SOLANA_GRPC_URL_MAIN, token: SOLANA_GRPC_TOKEN, ping: true }, { endpoint: SOLANA_GRPC_URL_SECONDARY, token: SOLANA_GRPC_TOKEN, ping: true }, { endpoint: 'https://solana-yellowstone-grpc.publicnode.com', token: '', ping: true } ] }; // Standard production settings const poolOptions = { pingIntervalMs: 30000, staleTimeoutMs: 120000, // 2 minutes - production safe deduplicationTtlMs: 60000, // 1 minute dedup window maxCacheSize: 10000, initialRetryDelayMs: 500, maxRetryDelayMs: 30000, retryBackoffFactor: 2 }; const pool = new src_1.GrpcPool(poolConfig, poolOptions); let transactionCount = 0; let duplicateCount = 0; // === TRANSACTION EVENT HANDLER === pool.on('transaction', (event) => { transactionCount++; const timestamp = new Date(event.timestamp).toISOString(); console.log('\n' + '='.repeat(80)); console.log(`šŸ”„ NEW OKX TRANSACTION #${transactionCount}`); console.log('='.repeat(80)); console.log(`šŸ“… Timestamp: ${timestamp}`); console.log(`šŸ”— Signature: ${event.signature}`); console.log(`šŸ“” Source: ${event.source.replace('https://', '')}`); // Process and debuffer the raw transaction data const debufferedData = debufferMessage(event.data); // Show transaction metadata if available if (event.data.meta) { console.log('\nšŸ“Š TRANSACTION METADATA:'); console.log(` Error: ${event.data.meta.err ? 'Failed' : 'Success'}`); console.log(` Slot: ${event.data.slot || 'N/A'}`); // Show fee information if available if (event.data.meta.fee !== undefined) { console.log(` Fee: ${event.data.meta.fee} lamports`); } // Show account balances changes if available if (event.data.meta.preBalances && event.data.meta.postBalances) { const balanceChanges = event.data.meta.postBalances.length; console.log(` Account Changes: ${balanceChanges} accounts affected`); } } // Show first few lines of debuffered transaction data console.log('\nšŸ“‹ TRANSACTION DATA (First 10 lines):'); const dataLines = JSON.stringify(debufferedData, null, 2).split('\n'); const previewLines = dataLines.slice(0, 10); previewLines.forEach((line, index) => { console.log(` ${String(index + 1).padStart(2, ' ')}: ${line}`); }); if (dataLines.length > 10) { console.log(` ... and ${dataLines.length - 10} more lines`); } console.log('\n' + '='.repeat(80)); }); // === DUPLICATE EVENT HANDLER === pool.on('duplicate', (event) => { duplicateCount++; const timestamp = new Date(event.timestamp).toISOString(); console.log(`šŸ”„ [${timestamp}] Filtered duplicate from ${event.source.replace('https://', '')}`); }); // === CONNECTION EVENT HANDLERS === pool.on('connected', () => { console.log('āœ… Pool connected - ready to receive OKX DEX transactions'); }); pool.on('disconnected', () => { console.log('šŸ”“ Pool disconnected'); }); pool.on('endpoint', (event) => { const timestamp = new Date(event.timestamp).toISOString(); const shortEndpoint = event.endpoint.replace('https://', '').split('.')[0]; const statusIcon = event.status === 'connected' ? '🟢' : event.status === 'reconnected' ? 'šŸ”„' : 'šŸ”“'; console.log(`${statusIcon} [${timestamp}] ${shortEndpoint}: ${event.status.toUpperCase()}`); }); // === ERROR HANDLER === pool.on('error', (error) => { console.error('āŒ Pool error:', error.message); }); try { // Connect to all endpoints console.log('šŸ”Œ Connecting to gRPC endpoints...'); await pool.connect(); // Subscribe to OKX DEX transactions console.log(`šŸŽÆ Subscribing to OKX DEX program: ${OKX_DEX_PROGRAM}`); await pool.subscribe({ accounts: {}, accountsDataSlice: [], transactions: { 'okx_dex_txns': { accountInclude: [OKX_DEX_PROGRAM], accountExclude: [], accountRequired: [], vote: false, failed: false } }, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, commitment: 'confirmed' }); console.log('āœ… OKX DEX subscription active!'); console.log('šŸ“ˆ Will display transaction details with debuffered data'); console.log('Press Ctrl+C to exit gracefully\n'); // Show periodic statistics const statsInterval = setInterval(() => { const total = transactionCount + duplicateCount; const uniquePercent = total > 0 ? Math.round((transactionCount / total) * 100) : 0; console.log(`\nšŸ“Š Statistics: ${transactionCount} unique, ${duplicateCount} duplicates (${uniquePercent}% unique)`); }, 60000); // Every minute // Graceful shutdown process.on('SIGINT', async () => { console.log('\nšŸ›‘ Shutting down OKX DEX subscriber...'); clearInterval(statsInterval); // Show final statistics const total = transactionCount + duplicateCount; console.log(`\nšŸ“Š Final Statistics:`); console.log(` Unique transactions: ${transactionCount}`); console.log(` Filtered duplicates: ${duplicateCount}`); console.log(` Total processed: ${total}`); await pool.close(); console.log('šŸ‘‹ OKX DEX subscriber stopped!'); process.exit(0); }); } catch (error) { console.error('āŒ Example failed:', error); process.exit(1); } } main().catch(console.error); //# sourceMappingURL=okx-dex-subscriber.js.map