UNPKG

@gala-chain/launchpad-sdk

Version:

TypeScript SDK for Gala Launchpad Backend API - Production-ready DeFi token launchpad integration with wallet-based authentication, GalaChain trading, and comprehensive user operations. 100% tested (22/22 endpoints working).

1,477 lines (1,137 loc) 60.6 kB
# Gala Launchpad SDK A comprehensive TypeScript SDK for the Gala Launchpad Backend API, providing type-safe authentication, trading, and real-time features for DeFi applications. [![npm version](https://badge.fury.io/js/@gala-chain%2Flaunchpad-sdk.svg)](https://badge.fury.io/js/@gala-chain%2Flaunchpad-sdk) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) ## Features **Clean Result Types with No Wrapper Overhead:** - **Direct Result Access**: Get clean, typed results without wrapper objects - **Semantic Type Conversion**: Dates as Date objects, numbers as numbers, strings for precision - **Comprehensive Type Safety**: Full TypeScript support with precise result interfaces - **Zero Wrapper Overhead**: No more `result.data.success` - direct property access - **Options Object Pattern**: All methods with 2+ parameters use clean options objects - **Auto-Pagination**: Automatic multi-page fetching with configurable concurrency ### Developer Experience ```typescript import { createLaunchpadSDK, createWallet } from '@gala-chain/launchpad-sdk'; // Auto-detect wallet format and create SDK const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' // Auto-detects format! }); // Direct result access - no wrapper objects! const pools = await sdk.fetchPools({ type: 'recent' }); console.log(`Found ${pools.total} pools`); // Direct property access console.log(`Page ${pools.page} of ${pools.totalPages}`); // Clean pagination console.log(`Has next: ${pools.hasNext}`); // Boolean convenience properties // Clean typed results everywhere const balance = await sdk.fetchGalaBalance(); console.log(`Balance: ${balance.balance} GALA`); // Direct balance access console.log(`Last updated: ${balance.lastUpdated.toISOString()}`); // Date object ``` ## Clean API Architecture **Direct result access with no wrapper overhead:** ```typescript // Get pools with direct property access const pools = await sdk.fetchPools({ type: 'recent' }); console.log(pools.pools); // Direct access to pool array console.log(pools.total); // Immediate pagination info console.log(pools.hasNext); // Computed convenience properties ``` ## Key Features - **Type-Safe API Client**: Full TypeScript support with comprehensive type definitions - **Clean Result Types**: Direct property access without wrapper objects - **Options Object Pattern**: All multi-parameter methods use clean options objects - **Auto-Pagination**: Automatic multi-page fetching for large result sets - **Signature Authentication**: Ethereum wallet-based authentication with automatic signature generation - **Helper Functions**: Auto-detecting wallet creation and SDK factory functions - **Pool Management**: Create, fetch, and check token pools on the launchpad - **Token Trading**: Buy and sell tokens with slippage protection via GalaChain - **Token Transfers**: Transfer GALA and launchpad tokens between wallets with EIP-712 signatures - **User Operations**: Portfolio management, token balances, and account management - **Comment System**: Post and retrieve comments on token pools - **Price History**: Fetch historical price data for DEX tokens with pagination (Node.js only, requires MySQL) - **Comprehensive Validation**: Input validation and error handling for all operations - **Multi-Environment Support**: Production, staging, and custom backend URLs ## Installation ### NPM ```bash npm install @gala-chain/launchpad-sdk ``` ### Yarn ```bash yarn add @gala-chain/launchpad-sdk ``` ### Peer Dependencies This SDK requires the following peer dependencies to be installed: ```bash npm install ethers@^6.15.0 @gala-chain/api@^2.4.3 @gala-chain/connect@^2.4.3 socket.io-client@^4.8.1 axios@^1.12.2 bignumber.js@^9.1.2 zod@^3.25.76 ``` **Or with yarn:** ```bash yarn add ethers@^6.15.0 @gala-chain/api@^2.4.3 @gala-chain/connect@^2.4.3 socket.io-client@^4.8.1 axios@^1.12.2 bignumber.js@^9.1.2 zod@^3.25.76 ``` **All peer dependencies are required** - this includes `socket.io-client` which is needed for transaction verification via WebSocket. ## Module Formats The SDK is distributed in **three module formats** to support both modern and legacy projects: ### ESM (ES Modules) - Primary Format **For modern bundlers and Node.js 16+:** ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); ``` **When to use:** - ✅ React, Vue, Svelte, Angular applications - ✅ Next.js, Nuxt, SvelteKit - ✅ Vite, Webpack, esbuild bundlers - ✅ Modern Node.js projects with `"type": "module"` ### CommonJS - Legacy Support **For CommonJS projects and older Node.js environments:** ```javascript const { createLaunchpadSDK } = require('@gala-chain/launchpad-sdk'); const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); ``` **When to use:** - ✅ Legacy Node.js projects with CommonJS modules - ✅ Older tooling that doesn't support ESM - ✅ Express.js, Nest.js (CommonJS mode) - ✅ Projects without build tools ### UMD (Universal Module Definition) - Browser Legacy **For browser globals and legacy environments:** ```html <script src="node_modules/@gala-chain/launchpad-sdk/dist/index.js"></script> <script> const sdk = window.GalaLaunchpadSDK.createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); </script> ``` **When to use:** - ✅ Direct browser `<script>` tags - ✅ Older browser environments - ✅ CDN delivery ### Module Resolution Node.js automatically selects the correct module format based on your project: | Project Type | Method | Format Used | File | |---|---|---|---| | ESM Module | `import` | ESM | `dist/index.esm.js` | | CommonJS | `require()` | CommonJS | `dist/index.cjs.js` | | Legacy Tools | Direct Include | UMD | `dist/index.js` | **No configuration needed** - Node.js and bundlers automatically select the optimal format via the package exports field! ## AI Agent Integration ### For Claude Desktop Users Install the MCP server to enable Claude to interact with Gala Launchpad directly: ```bash claude mcp add "galachain-launchpad" -- env PRIVATE_KEY=<YOUR_PRIVATE_KEY> ENVIRONMENT=development npx -y @gala-chain/launchpad-mcp-server@latest ``` **Environment Variables:** - `PRIVATE_KEY` - Your wallet private key (required) - `ENVIRONMENT` - Backend environment: `development` | `production` (default: development) - `DEBUG` - Enable debug logging: `true` | `false` (default: false) - `TIMEOUT` - Request timeout in milliseconds (default: 30000) **Features**: 50 tools + 14 slash commands for complete Gala Launchpad operations See: [MCP Server Documentation](../../mcp-server/README.md) **Try slash commands** (MCP v1.4.0+): ``` /galachain-launchpad:analyze-token tokenName=anime /galachain-launchpad:portfolio /galachain-launchpad:buy-tokens tokenName=anime galaAmount=100 ``` ### For AI Developers Need help with SDK integration, trading bots, or MCP server development? Ask **[@agent-galachain-launchpad-developer](../../@agent-galachain-launchpad-developer.md)** - a specialized AI agent with expertise in: - Complete SDK API (45 methods) - Trading patterns and DeFi best practices - MCP server architecture - Error handling strategies - Performance optimization **Full Integration Guide**: [AI Agent Guide](./docs/AI-AGENT-GUIDE.md) ## Quick Start ### Using Helper Functions (Recommended) ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; // Auto-detecting SDK creation (easiest method) const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' // Auto-detects format! }); // Clean, direct result access - All methods use options objects // Pool Management - Direct result properties const pools = await sdk.fetchPools({ type: 'recent', limit: 10 }); console.log(`Found ${pools.total} pools across ${pools.totalPages} pages`); console.log(`Current page: ${pools.page}, Has next: ${pools.hasNext}`); const badges = await sdk.fetchTokenBadges('dragnrkti'); console.log(`Volume badges: ${badges.volumeBadges.length}`); console.log(`Engagement badges: ${badges.engagementBadges.length}`); const details = await sdk.fetchPoolDetails('dragnrkti'); console.log(`Sale status: ${details.saleStatus}`); console.log(`Native token quantity: ${details.nativeTokenQuantity}`); // Price Calculations - Direct amount access const buyAmount = await sdk.calculateBuyAmount({ tokenName: 'dragnrkti', amount: '1', // 1 GALA type: 'native' }); console.log(`Buy amount: ${buyAmount.amount}`); console.log(`Transaction fee: ${buyAmount.transactionFee}`); // Trading Operations - Required expectedAmount and slippageToleranceFactor const buyResult = await sdk.buy({ tokenName: 'dragnrkti', amount: '1', type: 'native', expectedAmount: buyAmount.amount, // Required: from calculation slippageToleranceFactor: 0.05 // Required: decimal format (5% slippage) }); console.log(`Transaction ID: ${buyResult.transactionId}`); // Data & Analytics - Clean pagination const trades = await sdk.fetchTrades({ tokenName: 'dragnrkti' }); console.log(`Found ${trades.total} trades`); console.log(`Page ${trades.page} of ${trades.totalPages}`); trades.trades.forEach(trade => { console.log(`Trade: ${trade.tradeType} ${trade.tokenAmount} at ${trade.createdAt.toISOString()}`); }); const comments = await sdk.fetchComments({ tokenName: 'dragnrkti' }); console.log(`${comments.total} comments found`); // User Operations - Direct balance access const galaBalance = await sdk.fetchGalaBalance(); console.log(`GALA Balance: ${galaBalance.balance}`); console.log(`Last updated: ${galaBalance.lastUpdated.toISOString()}`); const tokenBalance = await sdk.fetchTokenBalance({ tokenName: 'dragnrkti', address: sdk.getAddress() }); console.log(`Token balance: ${tokenBalance.quantity}`); console.log(`USD value: $${tokenBalance.holdingPriceUsd}`); // Profile - Direct user data const profile = await sdk.fetchProfile(); console.log(`Profile name: ${profile.fullName || 'Not set'}`); // URL Utilities - Generate frontend URLs const tokenUrl = sdk.getUrlByTokenName('dragnrkti'); console.log(`View token: ${tokenUrl}`); // Output: https://lpad-frontend-test1.defi.gala.com/buy-sell/dragnrkti ``` ## Auto-Pagination Feature The SDK now supports automatic pagination for pool fetching with three powerful modes: ### Three Pagination Modes ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // MODE 1: Single Page (backward compatible) // Limit <= 20: Single API call const recent = await sdk.fetchPools({ type: 'recent', limit: 20 }); console.log(`Fetched ${recent.pools.length} pools in one request`); // MODE 2: Multi-Page Auto-Fetch // Limit > 20: Automatic concurrent multi-page fetching const large = await sdk.fetchPools({ type: 'popular', limit: 100 }); console.log(`Fetched ${large.pools.length} pools across multiple pages`); // Internally fetches 5 pages concurrently: 20+20+20+20+20 = 100 pools // MODE 3: Infinite Fetch // Limit = 0: Fetches ALL available pools const all = await sdk.fetchPools({ limit: 0 }); console.log(`Fetched all ${all.pools.length} pools from the platform`); // Convenience method for "fetch all" pattern const allRecent = await sdk.fetchAllPools({ type: 'recent' }); console.log(`Total pools: ${allRecent.total}`); ``` ### Concurrency Configuration The SDK uses `MAX_CONCURRENT_POOL_FETCHES` to control parallel API requests: ```typescript import { MAX_CONCURRENT_POOL_FETCHES } from '@gala-chain/launchpad-sdk'; console.log(`SDK fetches up to ${MAX_CONCURRENT_POOL_FETCHES} pages concurrently`); // Output: "SDK fetches up to 5 pages concurrently" ``` **Default**: 5 concurrent requests **Benefit**: Balances speed with API rate limits ### Performance Benefits | Scenario | Pages Fetched | Network Calls | Time (Sequential) | Time (Concurrent) | Improvement | |----------|---------------|---------------|-------------------|-------------------|-------------| | 20 pools | 1 page | 1 call | ~200ms | ~200ms | No change | | 100 pools | 5 pages | 5 calls | ~1000ms | ~200ms | 5x faster | | 500 pools | 25 pages | 25 calls | ~5000ms | ~1000ms | 5x faster | | All pools (1000+) | 50+ pages | 50+ calls | ~10,000ms | ~2000ms | 5x faster | ### When to Use Each Mode **Single Page (limit <= 20)** - Quick queries - UI pagination with next/previous buttons - When you only need recent results **Multi-Page (limit > 20)** - Analytics dashboards - Bulk operations on specific token counts - When you know how many results you need **Infinite (limit = 0 or fetchAllPools())** - Complete market scans - Full portfolio analysis - Trading bot initialization - Data exports and backups ### Example: Market Scanner ```typescript async function scanEntireMarket() { // Fetch all pools at once (auto-pagination handles everything) const allPools = await sdk.fetchAllPools({ type: 'popular' }); console.log(`Scanning ${allPools.total} pools...`); // Filter for interesting opportunities const highVolume = allPools.pools.filter(pool => parseFloat(pool.volumeGala) > 10000 ); console.log(`Found ${highVolume.length} high-volume pools`); return highVolume; } ``` ### New Methods **fetchAllPools(options?)** Convenience method that automatically fetches all available pools: ```typescript // Fetch all recent pools const allRecent = await sdk.fetchAllPools({ type: 'recent' }); // Fetch all pools matching search const dragons = await sdk.fetchAllPools({ search: 'dragon' }); // Fetch specific token across all results const specific = await sdk.fetchAllPools({ tokenName: 'anime' }); // Equivalent to fetchPools({ limit: 0 }) const all = await sdk.fetchAllPools(); ``` ### Manual SDK Creation (Alternative) ```typescript import { Wallet } from 'ethers'; import { LaunchpadSDK } from '@gala-chain/launchpad-sdk'; // Create wallet manually const wallet = new Wallet(process.env.PRIVATE_KEY); // Initialize SDK const sdk = new LaunchpadSDK({ wallet: wallet, baseUrl: 'https://lpad-backend-dev1.defi.gala.com', timeout: 30000, debug: false }); // Same clean API available const pools = await sdk.fetchPools({ type: 'recent' }); console.log(`${pools.total} pools found`); ``` ## Complete Example: Trading Flow with Clean Results ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; // 1. Create SDK with auto-detection const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // 2. Check available pools - direct access to results const pools = await sdk.fetchPools({ type: 'recent', limit: 10 }); console.log(`Found ${pools.total} pools across ${pools.totalPages} pages`); pools.pools.forEach(pool => { console.log(`Pool: ${pool.tokenName} created at ${pool.createdAt}`); }); // 3. Get price quote - direct amount access const quote = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native' }); console.log(`Buying 100 GALA worth will get you: ${quote.amount} TINYEVIL`); console.log(`Transaction fee: ${quote.transactionFee} GALA`); console.log(`Reverse bonding curve fee: ${quote.reverseBondingCurveFee} GALA`); // 4. Execute trade with slippage protection - requires expectedAmount const buyResult = await sdk.buy({ tokenName: 'tinyevil', amount: '100', type: 'native', expectedAmount: quote.amount, // Required: from calculation above slippageToleranceFactor: 0.05 // Required: decimal format for 5% slippage }); console.log(`Transaction submitted: ${buyResult.transactionId}`); // 5. Check trade history - clean pagination const trades = await sdk.fetchTrades({ tokenName: 'tinyevil' }); console.log(`Found ${trades.total} trades on page ${trades.page}`); console.log(`Has more pages: ${trades.hasNext}`); trades.trades.forEach(trade => { console.log(`${trade.tradeType}: ${trade.tokenAmount} at ${trade.createdAt.toISOString()}`); }); // 6. Post a comment about your trade const comment = await sdk.postComment({ tokenName: 'tinyevil', content: 'Just bought some tokens! Great project!' }); console.log(`Comment posted with ID: ${comment.id}`); // 7. Check your balance - direct balance access const galaBalance = await sdk.fetchGalaBalance(); console.log(`GALA Balance: ${galaBalance.balance}`); console.log(`Decimals: ${galaBalance.decimals}`); console.log(`Last updated: ${galaBalance.lastUpdated.toISOString()}`); const tokenBalance = await sdk.fetchTokenBalance({ tokenName: 'tinyevil', address: sdk.getAddress() }); console.log(`TINYEVIL Balance: ${tokenBalance.quantity}`); console.log(`USD Value: $${tokenBalance.holdingPriceUsd}`); console.log(`GALA Value: ${tokenBalance.holdingPriceGala} GALA`); console.log(`Finalized: ${tokenBalance.isFinalized}`); ``` ## Transfer Operations Transfer GALA and launchpad tokens between wallets with EIP-712 signatures: ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // Transfer GALA tokens - direct result access const galaTransfer = await sdk.transferGala({ recipientAddress: 'eth|1234567890abcdef1234567890abcdef12345678', // or 0x format amount: '1', // 1 GALA uniqueKey: 'galaconnect-operation-my-transfer-123' // Optional for idempotency }); console.log(`GALA Transfer ID: ${galaTransfer.transactionId}`); console.log(`Status: ${galaTransfer.status}`); // Transfer launchpad tokens - direct result access const tokenTransfer = await sdk.transferToken({ to: '0x9876543210fedcba9876543210fedcba98765432', // or eth| format tokenName: 'tinyevil', amount: '1000000', // Token amount in smallest unit uniqueKey: 'galaconnect-operation-token-transfer-456' // Optional for idempotency }); console.log(`Token Transfer ID: ${tokenTransfer.transactionId}`); console.log(`Status: ${tokenTransfer.status}`); ``` ### Transfer Features - **EIP-712 Signatures**: Secure blockchain transactions - **Address Format Handling**: Supports both `0x` and `eth|` formats - **Idempotency**: Optional unique keys prevent duplicate transfers (must use `galaswap-operation-` or `galaconnect-operation-` prefix) - **Comprehensive Validation**: Amount limits, address formats, token names, unique key formats - **GalaChain Integration**: Direct transfers via GalaChain gateway - **Error Handling**: Detailed error types for different failure scenarios ## Multi-Wallet Support The SDK supports **per-operation wallet overrides** for testing multi-wallet workflows without creating new SDK instances. This is ideal for: - Testing trading scenarios with multiple wallets - Building multi-user applications - Simulating different user behaviors - Creating automated trading bots ### Private Key Override Pattern All signing operations accept an optional `privateKey` parameter to use a different wallet for that specific operation: ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; // Create SDK with your main wallet const sdk = createLaunchpadSDK({ wallet: 'your-main-private-key' }); // Different wallet's private key (must be in '0x' + 64 hex format) const busterPrivateKey = '0x1234567890abcdef...'; // Buster's wallet // 1. Send GALA from main wallet to Buster await sdk.transferGala({ recipientAddress: '0xBusterAddress...', amount: '1000' // Uses main SDK wallet (no privateKey override) }); // 2. Have Buster buy tokens using his own wallet const buyResult = await sdk.buy({ tokenName: 'tinyevil', amount: '100', type: 'native', expectedAmount: '500000', slippageToleranceFactor: 0.05, privateKey: busterPrivateKey // Override to use Buster's wallet }); // 3. Have Buster post a comment await sdk.postComment({ tokenName: 'tinyevil', content: 'Great buy!', privateKey: busterPrivateKey // Buster posts the comment }); // 4. Main wallet continues operations normally const mainWalletBalance = await sdk.fetchGalaBalance(); // Uses main SDK wallet address ``` ### Supported Operations with Private Key Override All signing operations support the `privateKey` parameter: **Trading Operations:** - `buy(options)` - Buy tokens with different wallet - `sell(options)` - Sell tokens with different wallet **Token Creation:** - `launchToken(data)` - Create token from different wallet - `uploadTokenImage(options)` - Upload image for token **Transfer Operations:** - `transferGala(options)` - Transfer GALA from different wallet - `transferToken(options)` - Transfer tokens from different wallet **Social & Profile:** - `postComment(options)` - Post comment from different wallet - `updateProfile(data)` - Update profile for different wallet - `uploadProfileImage(options)` - Upload profile image for different wallet ### Complete Multi-Wallet Example ```typescript import { createLaunchpadSDK, createWallet } from '@gala-chain/launchpad-sdk'; // Main SDK instance const sdk = createLaunchpadSDK({ wallet: 'your-main-private-key' }); // Create a test wallet for "Buster" const busterWallet = createWallet(); // Random wallet const busterPrivateKey = busterWallet.privateKey; const busterAddress = busterWallet.address; console.log(`Buster's address: ${busterAddress}`); // 1. Fund Buster with GALA from main wallet await sdk.transferGala({ recipientAddress: busterAddress, amount: '1000' }); // 2. Send Buster some tokens from main wallet await sdk.transferToken({ to: busterAddress, tokenName: 'tinyevil', amount: '10000' }); // 3. Have Buster send some tokens back to main wallet await sdk.transferToken({ to: sdk.getEthereumAddress(), // Main wallet address tokenName: 'tinyevil', amount: '5000', privateKey: busterPrivateKey // Buster's wallet signs }); // 4. Have Buster buy more tokens const buyQuote = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native' }); await sdk.buy({ tokenName: 'tinyevil', amount: '100', type: 'native', expectedAmount: buyQuote.amount, slippageToleranceFactor: 0.05, privateKey: busterPrivateKey // Buster buys }); // 5. Have Buster sell some tokens const sellQuote = await sdk.calculateSellAmount({ tokenName: 'tinyevil', amount: '50', type: 'native' }); await sdk.sell({ tokenName: 'tinyevil', amount: '50', type: 'native', expectedAmount: sellQuote.amount, slippageToleranceFactor: 0.05, privateKey: busterPrivateKey // Buster sells }); // 6. Check final balances for both wallets const mainBalance = await sdk.fetchGalaBalance(); // Main wallet const busterBalance = await sdk.fetchGalaBalance(busterAddress); // Buster's wallet console.log(`Main wallet: ${mainBalance.balance} GALA`); console.log(`Buster wallet: ${busterBalance.balance} GALA`); ``` ### Private Key Format Requirements The `privateKey` parameter must be a string in the format: - **Format**: `'0x' + 64 hexadecimal characters` - **Example**: `'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'` - **Invalid**: Raw hex without '0x' prefix, mnemonic phrases, addresses ```typescript // ✅ Valid private key formats const validKey1 = '0x' + 'a'.repeat(64); // Correct format const validKey2 = wallet.privateKey; // From ethers.js Wallet // ❌ Invalid formats (will throw validation error) const invalidKey1 = 'a'.repeat(64); // Missing '0x' prefix const invalidKey2 = 'word1 word2 ... word24'; // Mnemonic not accepted const invalidKey3 = '0x123'; // Too short ``` ## Available Methods & Result Types ### **Fetch Operations** ```typescript // Pool Management fetchPools(options?): Promise<PoolsResult> // Returns: { pools, page, limit, total, totalPages, hasNext, hasPrevious } // Supports auto-pagination: limit <= 20 (single page), limit > 20 (multi-page), limit = 0 (all pools) fetchAllPools(options?): Promise<PoolsResult> // Returns: { pools, page, limit, total, totalPages, hasNext, hasPrevious } // Convenience method that fetches ALL pools (equivalent to fetchPools({ limit: 0 })) fetchTokenDistribution(tokenName): Promise<TokenDistributionResult> // Returns: { holders, totalSupply, totalHolders, lastUpdated } fetchTokenBadges(tokenName): Promise<TokenBadgesResult> // Returns: { volumeBadges, engagementBadges } fetchPoolDetails(tokenName): Promise<PoolDetailsData> // Returns: { basePrice, maxSupply, saleStatus, nativeTokenQuantity, ... } fetchVolumeData(options): Promise<GraphDataResult> // Returns: { dataPoints } // Trade & User Data fetchTrades(options): Promise<TradesResult> // Returns: { trades, page, limit, total, totalPages, hasNext, hasPrevious } fetchGalaBalance(address?): Promise<GalaBalanceInfo> // Returns: { userAddress, balance, decimals, lastUpdated } fetchTokenBalance(options): Promise<TokenBalanceInfo> // Returns: { quantity, holdingPriceUsd, holdingPriceGala, isFinalized, ... } fetchComments(options): Promise<CommentsResult> // Returns: { comments, page, limit, total, totalPages, hasNext, hasPrevious } fetchProfile(address?): Promise<UserProfile> // Returns: { fullName, profileImage, address, ... } fetchLaunchTokenFee(): Promise<number> // Returns: Current GALA fee required to launch a new token (e.g., 0.001) ``` ### **Calculate Operations** ```typescript // Price Calculations calculateBuyAmount(options): Promise<AmountCalculationResult> // Returns: { amount, reverseBondingCurveFee, transactionFee } calculateSellAmount(options): Promise<AmountCalculationResult> // Returns: { amount, reverseBondingCurveFee, transactionFee } calculateInitialBuyAmount(options): Promise<AmountCalculationResult> // Returns: { amount, reverseBondingCurveFee, transactionFee } calculateBuyAmountForGraduation(tokenName): Promise<AmountCalculationResult> // Returns: { amount, reverseBondingCurveFee, transactionFee } // Calculates exact GALA cost to buy all remaining tokens and graduate pool // Local Calculations (Client-Side, No Network) calculateBuyAmountLocal(options): Promise<AmountCalculationResult> // Options: { tokenName, amount, type: 'native' | 'exact' } // Returns: { amount, reverseBondingCurveFee: '0', transactionFee, gasFee } // Instant buy calculation using local bonding curve formulas calculateSellAmountLocal(options): Promise<AmountCalculationResult> // Options: { tokenName, amount, type: 'native' | 'exact', maxSupply, minFeePortion, maxFeePortion } // Returns: { amount, reverseBondingCurveFee, transactionFee, gasFee } // Instant sell calculation with reverse bonding curve fees // External Calculations (GalaChain Network) calculateBuyAmountExternal(options): Promise<AmountCalculationResult> // Explicit external calculation wrapper (same as calculateBuyAmount) calculateSellAmountExternal(options): Promise<AmountCalculationResult> // Explicit external calculation wrapper (same as calculateSellAmount) // Note: Pass `calculateAmountMode: 'local' | 'external'` to override SDK default mode // Price History Operations (MySQL-based, Node.js only) fetchPriceHistory(options): Promise<PriceHistoryResult> // Options: { tokenId, from?, to?, sortOrder?, page?, limit? } // Returns: { snapshots, page, limit, total, totalPages, hasNext, hasPrevious } // Fetches paginated historical price snapshots from MySQL database fetchAllPriceHistory(options): Promise<PriceHistoryResult> // Options: { tokenId, from?, to?, sortOrder? } (no pagination params) // Returns: All matching snapshots with total count // Convenience method with automatic pagination (returns ALL snapshots) fetchPrices(options?): Promise<PricesResult> // Options: { page?, limit?, sortByType? } // Returns: { snapshots, page, limit, total, totalPages, hasNext, hasPrevious } // Fetches latest price for each unique token (paginated) fetchAllPrices(): Promise<PricesResult> // No parameters // Returns: All latest prices combined in single result // Convenience method with automatic pagination (returns ALL latest prices) // Note: Price history methods require MySQL connection string in SDK config // See MySQL Configuration section below for setup details ``` ## Performance Optimization ### Reusing Pool Data to Avoid Redundant Network Calls Methods that internally use `calculateBuyAmount`/`calculateSellAmount` now support optional `calculateAmountMode` and `currentSupply` parameters for performance optimization. This allows you to: 1. **Fetch pool details once** using `fetchPoolDetailsForCalculation` 2. **Reuse `currentSupply`** across multiple calculations 3. **Eliminate redundant network calls** with local mode calculations 4. **Get instant results** when real-time precision isn't required ### Supported Methods The following methods accept optional performance parameters: - `fetchLaunchpadTokenSpotPrice(options)` - Pass `calculateAmountMode` and/or `currentSupply` - `calculateBuyAmountForGraduation(options)` - Pass `calculateAmountMode` and/or `currentSupply` - `graduateToken(options)` - Pass `calculateAmountMode` and/or `currentSupply` ### Using CALCULATION_MODES Constant ```typescript import { createLaunchpadSDK, CALCULATION_MODES } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // Use type-safe calculation mode constants console.log(CALCULATION_MODES.LOCAL); // 'local' console.log(CALCULATION_MODES.EXTERNAL); // 'external' ``` ### Basic Optimization Pattern ```typescript import { createLaunchpadSDK, CALCULATION_MODES } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); const tokenName = 'tinyevil'; // ❌ WITHOUT OPTIMIZATION (3 network calls) const spotPrice1 = await sdk.fetchLaunchpadTokenSpotPrice(tokenName); // → Fetches pool details internally (call #1) const graduation1 = await sdk.calculateBuyAmountForGraduation(tokenName); // → Fetches pool details internally (call #2) const graduationResult1 = await sdk.graduateToken({ tokenName }); // → Fetches pool details internally (call #3) // ✅ WITH OPTIMIZATION (1 network call + instant local calculations) // Step 1: Fetch pool details once const poolDetails = await sdk.fetchPoolDetailsForCalculation(tokenName); const currentSupply = poolDetails.currentSupply; // Pre-computed with full precision // Step 2: Reuse currentSupply for instant local calculations const spotPrice2 = await sdk.fetchLaunchpadTokenSpotPrice({ tokenName, calculateAmountMode: CALCULATION_MODES.LOCAL, currentSupply }); // → Uses local bonding curve formulas (instant, no network call) const graduation2 = await sdk.calculateBuyAmountForGraduation({ tokenName, calculateAmountMode: CALCULATION_MODES.LOCAL, currentSupply }); // → Uses local calculation (instant, no network call) // Step 3: Graduate with optimized calculation const graduationResult2 = await sdk.graduateToken({ tokenName, calculateAmountMode: CALCULATION_MODES.LOCAL, currentSupply, slippageToleranceFactor: 0.01 }); // → Skips redundant pool fetch, uses local calculation ``` ### Performance Comparison | Pattern | Network Calls | Speed | Use Case | |---------|--------------|-------|----------| | **Without Optimization** | 3 calls | ~600-900ms | One-off operations | | **With Optimization** | 1 call | ~200ms | Batch operations, price discovery | | **Reduction** | **66% fewer calls** | **~70% faster** | | ### Complete Optimization Example ```typescript import { createLaunchpadSDK, CALCULATION_MODES } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); async function optimizedPoolAnalysis(tokenName: string) { console.time('Optimized Analysis'); // 1. Fetch pool details once (only network call needed) const poolDetails = await sdk.fetchPoolDetailsForCalculation(tokenName); const { currentSupply, maxSupply, reverseBondingCurveMinFeeFactor, reverseBondingCurveMaxFeeFactor } = poolDetails; // 2. Get spot price with local calculation (instant) const spotPrice = await sdk.fetchLaunchpadTokenSpotPrice({ tokenName, calculateAmountMode: CALCULATION_MODES.LOCAL, currentSupply }); console.log(`Spot price: $${spotPrice.usdPrice.toFixed(6)}`); // 3. Calculate graduation cost with local calculation (instant) const graduationCost = await sdk.calculateBuyAmountForGraduation({ tokenName, calculateAmountMode: CALCULATION_MODES.LOCAL, currentSupply }); console.log(`Graduation cost: ${graduationCost.amount} GALA`); // 4. Simulate multiple buy scenarios (all instant) const buyAmounts = ['10', '100', '1000']; for (const amount of buyAmounts) { const buyCalc = await sdk.calculateBuyAmount({ tokenName, amount, type: 'native', mode: CALCULATION_MODES.LOCAL, // Can also use 'mode' alias currentSupply }); console.log(`Buying ${amount} GALA gets you: ${buyCalc.amount} tokens`); } // 5. Simulate multiple sell scenarios (all instant) const sellAmounts = ['10', '100', '1000']; for (const amount of sellAmounts) { const sellCalc = await sdk.calculateSellAmountLocal({ tokenName, amount, type: 'native', maxSupply, reverseBondingCurveMinFeeFactor, reverseBondingCurveMaxFeeFactor }); console.log(`Selling ${amount} GALA worth gets you: ${sellCalc.amount} GALA`); } console.timeEnd('Optimized Analysis'); // Output: Optimized Analysis: ~200-300ms (vs ~2-3 seconds without optimization) } optimizedPoolAnalysis('tinyevil'); ``` ### When to Use Optimization **✅ Use optimization when:** - Performing multiple calculations on the same token - Building price discovery tools or analytics dashboards - Running simulations or backtests - Creating trading bots with frequent calculations - Displaying real-time price feeds (use local mode for smooth updates) **❌ Skip optimization when:** - Performing a single calculation - Requiring absolute precision from GalaChain network - Token supply changes frequently between calls ### Local vs External Calculations ```typescript // Local mode (client-side, instant, <0.01% difference from external) const localCalc = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native', mode: CALCULATION_MODES.LOCAL, currentSupply: poolDetails.currentSupply }); // External mode (GalaChain network, real-time, slower) const externalCalc = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native', mode: CALCULATION_MODES.EXTERNAL // or omit to use SDK default }); // Accuracy comparison const difference = Math.abs( parseFloat(localCalc.amount) - parseFloat(externalCalc.amount) ); const percentDiff = (difference / parseFloat(externalCalc.amount)) * 100; console.log(`Difference: ${percentDiff.toFixed(4)}%`); // Typically < 0.01% ``` ### SDK Configuration for Default Calculation Mode ```typescript import { createLaunchpadSDK, CALCULATION_MODES } from '@gala-chain/launchpad-sdk'; // Configure SDK to use local calculations by default const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic', config: { calculateAmountMode: CALCULATION_MODES.LOCAL // Default to local mode } }); // All calculations will use local mode by default const calc1 = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native' // Uses LOCAL mode from SDK config }); // Override per-call if needed const calc2 = await sdk.calculateBuyAmount({ tokenName: 'tinyevil', amount: '100', type: 'native', mode: CALCULATION_MODES.EXTERNAL // Override to external for this call }); ``` ## Token Metadata Cache The SDK includes an **intelligent metadata cache** that eliminates redundant API calls and enables instant local calculations. The cache stores immutable token metadata (reverse bonding curve fees, vault addresses, max supply) that never changes. ### How Cache Warming Works **Opportunistic & Zero-Cost**: Cache warming happens automatically during normal SDK operations without any additional network requests. When you call `fetchPools()`, the SDK extracts and caches metadata from the pool data that was already fetched. ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // Before fetching - cache is empty let cacheInfo = sdk.getCacheInfo(); console.log('Cached tokens:', cacheInfo.totalTokens); // 0 // Fetch pools - automatically warms cache with RBC fees and vault addresses await sdk.fetchPools({ type: 'recent', limit: 20 }); // After fetching - cache is warmed with 20 tokens cacheInfo = sdk.getCacheInfo(); console.log('Cached tokens:', cacheInfo.totalTokens); // 20 console.log('Cache size:', (cacheInfo.cacheSize / 1024).toFixed(2), 'KB'); // Now local calculations are instant (no network calls required) const calc = await sdk.calculateBuyAmountLocal({ tokenName: 'anime', amount: '100', type: 'native', currentSupply: '500000' }); console.log('Instant result:', calc.amount); // <1ms, used cached RBC fees ``` ### Performance Benefits **Dramatic Performance Improvements:** | Operation | Without Cache | With Cache | Improvement | |-----------|---------------|------------|-------------| | Single calculation | ~200ms | <1ms | **200x faster** | | 10 calculations | ~2000ms | <5ms | **400x faster** | | Price discovery (50 tokens) | ~10,000ms | ~50ms | **200x faster** | **Zero Network Overhead**: Cache warming extracts metadata from pool responses you're already receiving - no extra API calls required. **Memory Efficient**: - ~220 bytes per cached token (includes RBC fees + vault address) - Max 10,000 tokens = ~2.5 MB total memory - LRU eviction keeps memory bounded ### Cache Lifecycle **Session Lifetime**: - Cache persists for entire MCP server session (survives across conversations) - For direct SDK usage, cache lifetime matches SDK instance lifetime - Call `sdk.cleanup()` to clear cache and release resources **LRU Eviction**: - Maximum 10,000 tokens cached - Least Recently Updated (LRU) tokens evicted when limit reached - O(1) get/set/eviction using JavaScript Map insertion order ```typescript // Cache automatically manages size await sdk.fetchPools({ limit: 100 }); // Warms cache with 100 tokens await sdk.fetchPools({ limit: 100 }); // Warms with 100 more tokens await sdk.fetchPools({ limit: 100 }); // ... continues // When 10,000 limit reached, oldest tokens are evicted automatically const stats = sdk.getCacheInfo(); console.log('Total tokens:', stats.totalTokens); // Never exceeds 10,000 console.log('Oldest entry:', new Date(stats.oldestEntry).toISOString()); ``` ### Cache Management API **Get Cache Statistics:** ```typescript const stats = sdk.getCacheInfo(); console.log('Total tokens cached:', stats.totalTokens); console.log('Memory usage:', stats.cacheSize, 'bytes'); console.log('Average per entry:', (stats.cacheSize / stats.totalTokens).toFixed(0), 'bytes'); console.log('Oldest entry:', new Date(stats.oldestEntry).toISOString()); ``` **Clear Cache (Selective or Complete):** ```typescript // Clear specific token sdk.clearCache('anime'); console.log('Cleared anime from cache'); // Clear all tokens sdk.clearCache(); console.log('Cleared entire cache'); // Verify cache is empty const stats = sdk.getCacheInfo(); console.log('Tokens remaining:', stats.totalTokens); // 0 ``` **Manual Cache Warming (Advanced):** ```typescript // Manually warm cache from pool data (useful for custom caching scenarios) sdk.warmCacheFromPoolData('mytoken', { vaultAddress: 'service|Token$Unit$MYTOKEN$...', reverseBondingCurveMinFeeFactor: 0.0, reverseBondingCurveMaxFeeFactor: 0.5 }); // Cache is now warmed for local calculations const calc = await sdk.calculateBuyAmountLocal({ tokenName: 'mytoken', amount: '100', type: 'native', currentSupply: '1000000' }); ``` ### Complete Cache Example ```typescript import { createLaunchpadSDK, CALCULATION_MODES } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key-or-mnemonic' }); // 1. Warm cache by fetching pools console.log('Warming cache...'); await sdk.fetchPools({ type: 'popular', limit: 50 }); // 2. Check cache statistics const stats = sdk.getCacheInfo(); console.log(`Cache warmed with ${stats.totalTokens} tokens`); console.log(`Memory: ${(stats.cacheSize / 1024).toFixed(2)} KB`); // 3. Perform instant calculations using cached metadata const tokens = ['anime', 'dragnrkti', 'rocketri']; console.log('\nCalculating spot prices (instant, no network calls):'); console.time('Batch Calculations'); for (const token of tokens) { // Get pool details once for this token const poolDetails = await sdk.fetchPoolDetailsForCalculation(token); // Multiple instant calculations using cached data const [buy10, buy100, buy1000] = await Promise.all([ sdk.calculateBuyAmountLocal({ tokenName: token, amount: '10', type: 'native', currentSupply: poolDetails.currentSupply }), sdk.calculateBuyAmountLocal({ tokenName: token, amount: '100', type: 'native', currentSupply: poolDetails.currentSupply }), sdk.calculateBuyAmountLocal({ tokenName: token, amount: '1000', type: 'native', currentSupply: poolDetails.currentSupply }) ]); console.log(`${token}:`); console.log(` 10 GALA → ${buy10.amount} tokens`); console.log(` 100 GALA → ${buy100.amount} tokens`); console.log(` 1000 GALA → ${buy1000.amount} tokens`); } console.timeEnd('Batch Calculations'); // Output: Batch Calculations: ~50-100ms (vs ~3-5 seconds without cache) // 4. Clear cache when done (optional) sdk.clearCache(); console.log('\nCache cleared'); ``` ### Cache Design Philosophy **Immutable Data Only**: Only caches data that never changes: - ✅ Reverse bonding curve fee factors (set at token creation) - ✅ Vault addresses (permanent token identifiers) - ✅ Max supply (bonding curve constant, typically 10M) - ❌ Current supply (changes with every trade - fetched dynamically) - ❌ Token balances (user-specific and dynamic) **Zero-Cost Architecture**: No extra API calls required for cache warming. Metadata is extracted from responses you're already receiving during normal operations. **Bounded Memory**: LRU eviction ensures cache never exceeds 10,000 tokens (~2.5 MB), making it safe for long-running applications. **Smart Defaults**: Uses 10,000,000 as default max supply for tokens without explicit data (matches 99%+ of launchpad tokens). ### When to Use the Cache **✅ Perfect for:** - Price discovery and analytics dashboards - Trading bots with frequent calculations - Batch operations on multiple tokens - Real-time price feeds and charts - Simulation and backtesting tools **❌ Not needed for:** - Single one-off calculations - Operations requiring absolute real-time precision - Scenarios where current supply changes between calls ### Cache Warming Demo Run the complete cache demo to see all features in action: ```bash npm run demo-cache ``` The demo showcases: - Opportunistic warming from `fetchPools()` - Detailed warming from `fetchPoolDetailsForCalculation()` - Token name normalization (case-insensitive) - Cache hit performance (200x+ faster than network calls) - Memory estimation accuracy - LRU eviction behavior - Clear cache operations ### **Trading Operations** ```typescript // Buy/Sell Tokens buy(options): Promise<TransactionResult> // Options: { tokenName, amount, type, expectedAmount, slippageToleranceFactor } // Returns: { transactionId, status, ... } sell(options): Promise<TransactionResult> // Options: { tokenName, amount, type, expectedAmount, slippageToleranceFactor } // Returns: { transactionId, status, ... } graduateToken(options): Promise<TransactionResult> // Options: { tokenName, slippageToleranceFactor?, maxAcceptableReverseBondingCurveFeeSlippageFactor?, privateKey? } // Returns: { transactionId, status, ... } // One-step pool graduation: calculates cost and buys all remaining tokens ``` ### **Content Operations** ```typescript // Comments & Content postComment(options): Promise<CommentResult> // Returns: { id, content, createdAt, ... } // Token Creation & Management launchToken(data): Promise<LaunchTokenResult> // Returns: { transactionId, status, ... } uploadTokenImage(options): Promise<ImageUploadResult> // Returns: { imageUrl, success, ... } // Profile Management updateProfile(data): Promise<ProfileUpdateResult> // Returns: { success, data, ... } uploadProfileImage(options): Promise<ImageUploadResult> // Returns: { imageUrl, success, ... } ``` ### **Validation & Utilities** - `isTokenNameAvailable(tokenName: string): Promise<boolean>` - `isTokenSymbolAvailable(symbol: string): Promise<boolean>` - `isTokenGraduated(tokenName: string): Promise<boolean>` - Check if token completed bonding curve phase - `getAddress(): string` - Get backend format address (eth|...) - `getEthereumAddress(): string` - Get Ethereum format address (0x...) - `getUrlByTokenName(tokenName: string): string` - Get frontend URL for token - `resolveVaultAddress(tokenName: string): Promise<string>` - `cleanup(): void` - Cleanup resources ## Configuration ### Constants ```typescript import { MAX_CONCURRENT_POOL_FETCHES } from '@gala-chain/launchpad-sdk'; console.log(`SDK fetches up to ${MAX_CONCURRENT_POOL_FETCHES} pages concurrently`); // Output: "SDK fetches up to 5 pages concurrently" ``` **MAX_CONCURRENT_POOL_FETCHES** - **Type**: `number` - **Value**: `5` - **Purpose**: Controls concurrency for parallel page fetching in auto-pagination - **Balances**: Speed vs API rate limits - **Usage**: Exported constant for reference (not configurable at runtime) ### SDK Configuration ```typescript interface LaunchpadSDKConfig { wallet: Wallet; // ethers.js Wallet instance baseUrl?: string; // Backend URL (default: dev environment) galaChainBaseUrl?: string; // GalaChain gateway URL bundleBaseUrl?: string; // Bundle service URL webSocketUrl?: string; // WebSocket URL for monitoring timeout?: number; // Request timeout (default: 30000ms) debug?: boolean; // Enable debug logging maxRetries?: number; // Retry attempts (default: 3) retryDelay?: number; // Retry delay (default: 1000ms) mysqlConnectionString?: string; // MySQL connection string for price history (Node.js only) // Format: mysql://user:password@host:port/database } ``` ### MySQL Configuration (Price History) To use the price history methods, you need to configure a MySQL connection string: ```typescript import { createLaunchpadSDK } from '@gala-chain/launchpad-sdk'; const sdk = createLaunchpadSDK({ wallet: 'your-private-key', config: { baseUrl: 'https://lpad-backend-dev1.defi.gala.com', mysqlConnectionString: 'mysql://user:password@localhost:3306/galachain' } }); // 1. Fetch historical price snapshots (paginated) const priceHistory = await sdk.fetchPriceHistory({ tokenId: 'Token|Unit|GUSDC|eth:9401b171307bE656f00F9e18DF756643FD3a91dE', // String format from: new Date('2024-01-01'), to: new Date('2024-01-31'), sortOrder: 'DESC', page: 1, limit: 20 }); console.log(`Found ${priceHistory.snapshots.length} snapshots`); console.log(`Total: ${priceHistory.total}, Page ${priceHistory.page} of ${priceHistory.totalPages}`); // 2. Fetch ALL historical price snapshots (auto-paginated) const allHistory = await sdk.fetchAllPriceHistory({ tokenId: { // Object format also supported collection: 'Token', category: 'Unit', type: 'GUSDC', additionalKey: 'eth:9401b171307bE656f00F9e18DF756643FD3a91dE' }, from: new Date('2024-01-01'), to: new Date('2024-01-31') }); console.log(`Total snapshots: ${allHistory.total}`); // 3. Fetch latest prices for all tokens (paginated) const latestPrices = await sdk.fetchPrices({ page: 1, limit: 20, sortByType: 'ASC' // Optional: sort alphabetically by token type }); console.log(`Found ${latestPrices.snapshots.length} tokens with prices`); // 4. Fetch all latest prices (auto-paginated) const allLatestPrices = await sdk.fetchAllPrices(); console.log(`Total tokens with prices: ${allLatestPrices.total}`); ``` **Token Identification Formats:** ```typescript // String format (pipe-delimited) - simpler tokenId: 'Token|Unit|GUSDC|eth:9401b171307bE656f00F9e18DF756643FD3a91dE' // Object format (explicit) - for clarity tokenId: { collection: 'Token', category: 'Unit', type: 'GUSDC', additionalKey: 'eth:9401b171307bE656f00F9e18DF756643FD3a91dE' } ``` **Environment Configuration:** ```bash # Set in .env or pass to SDK config PRICE_SERVICE_MYSQL_CONNECTION_STRING=mysql://user:password@localhost:3306/galachain ``` **Requirements:** - Node.js environment (browser not supported) - MySQL server with `price_snapshots` table - Connection pooling is automatic (default 5 concurrent connections) ### Security Considerations ⚠️ **Credential Management:** - Store MySQL connection strings in environment variables, NOT in code - Use restrictive file permissions on `.env` files (recommended: `0600`) - Never log or expose connection strings in debug output - For production, consider using credential providers (AWS Secrets Manager, Azure Key Vault, etc.) ⚠️ **Input Validation:** - TokenClassKey fields are validated with a whitelist pattern to prevent SQL injection - Only alphanumeric characters plus dots (.), colons (:), hyphens (-), and underscores (_) are allowed - Fields are limited to 255 characters maximum ⚠️ **Database Security:** - Ensure MySQL is behind a firewall and not exposed to the internet - Use strong passwords and rotate them regularly - Consider using SSL/TLS for MySQL connections in production - Create a dedicated dat